1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/ccn_lite/ccnl-core.c
Ludwig Ortmann cf637942cc *: add missing blank lines
Some functions had no blank lines to separate them from their
neighborhood.
2015-03-21 16:34:59 +01:00

1498 lines
43 KiB
C

/*
* @f ccnl-core.c
* @b CCN lite, core CCNx protocol logic
*
* Copyright (C) 2011-13, Christian Tschudin, University of Basel
* Copyright (C) 2013, Christian Mehlis, Freie Universität Berlin
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* File history:
* 2011-04-09 created
*/
#define CCNL_VERSION "2013-07-27"
#include <string.h>
#include <stdio.h>
#include "ccnl.h"
#include "ccnl-core.h"
#include "ccnl-pdu.h"
#include "ccnx.h"
#include "ccnl-ext.h"
#include "ccnl-platform.h"
#include "ccnl-includes.h"
#include "ccnl-riot-compat.h"
#define CCNL_DYNAMIC_FIB (0)
static struct ccnl_interest_s *ccnl_interest_remove(struct ccnl_relay_s *ccnl,
struct ccnl_interest_s *i);
void ccnl_ll_TX(struct ccnl_relay_s *ccnl, struct ccnl_if_s *ifc,
sockunion *dest, struct ccnl_buf_s *buf);
void free_2ptr_list(void *a, void *b)
{
ccnl_free(a);
ccnl_free(b);
}
void free_3ptr_list(void *a, void *b, void *c)
{
ccnl_free(a);
ccnl_free(b);
ccnl_free(c);
}
void free_4ptr_list(void *a, void *b, void *c, void *d)
{
ccnl_free(a);
ccnl_free(b);
ccnl_free(c);
ccnl_free(d);
}
void free_prefix(struct ccnl_prefix_s *p)
{
if (p) {
free_4ptr_list(p->path, p->comp, p->complen, p);
}
}
void free_content(struct ccnl_content_s *c)
{
free_prefix(c->name);
free_2ptr_list(c->pkt, c);
}
void free_forward(struct ccnl_forward_s *fwd)
{
free_prefix(fwd->prefix);
ccnl_free(fwd);
}
// ----------------------------------------------------------------------
// datastructure support functions
struct ccnl_buf_s *
ccnl_buf_new(void *data, int len)
{
struct ccnl_buf_s *b = (struct ccnl_buf_s *) ccnl_malloc(sizeof(*b) + len);
if (!b) {
return NULL;
}
b->next = NULL;
b->datalen = len;
if (data) {
memcpy(b->data, data, len);
}
return b;
}
struct ccnl_buf_s *buf_dup(struct ccnl_buf_s *B)
{
return (B) ? ccnl_buf_new(B->data, B->datalen) : NULL;
}
int buf_equal(struct ccnl_buf_s *X, struct ccnl_buf_s *Y)
{
return ((X) && (Y) && (X->datalen == Y->datalen) && !memcmp(X->data, Y->data, X->datalen));
}
int ccnl_prefix_cmp(struct ccnl_prefix_s *name, unsigned char *md,
struct ccnl_prefix_s *p, int mode)
/* returns -1 if no match at all (all modes) or exact match failed
returns 0 if full match (CMP_EXACT)
returns n>0 for matched components (CMP_MATCH, CMP_LONGEST) */
{
int i, clen, nlen = name->compcnt + (md ? 1 : 0), rc = -1;
unsigned char *comp;
if (mode == CMP_EXACT && nlen != p->compcnt) {
goto done;
}
for (i = 0; i < nlen && i < p->compcnt; i++) {
comp = i < name->compcnt ? name->comp[i] : md;
clen = i < name->compcnt ? name->complen[i] : 32; // SHA256_DIGEST_LEN
if (clen != p->complen[i] || memcmp(comp, p->comp[i], p->complen[i])) {
rc = mode == CMP_EXACT ? -1 : i;
goto done;
}
}
rc = (mode == CMP_EXACT) ? 0 : i;
done:
DEBUGMSG(49,
"ccnl_prefix_cmp (mode=%d, nlen=%d, plen=%d, %d), name=%s: %d (%p)\n",
mode, nlen, p->compcnt, name->compcnt, ccnl_prefix_to_path(name),
rc, md);
return rc;
}
// ----------------------------------------------------------------------
// ccnb parsing support
static int hunt_for_end(unsigned char **buf, int *len, unsigned char **valptr,
int *vallen)
{
int typ, num;
while (dehead(buf, len, &num, &typ) == 0) {
if (num == 0 && typ == 0) {
return 0;
}
if (consume(typ, num, buf, len, valptr, vallen) < 0) {
return -1;
}
}
return -1;
}
int consume(int typ, int num, unsigned char **buf, int *len,
unsigned char **valptr, int *vallen)
{
if (typ == CCN_TT_BLOB || typ == CCN_TT_UDATA) {
if (valptr) {
*valptr = *buf;
}
if (vallen) {
*vallen = num;
}
*buf += num, *len -= num;
return 0;
}
if (typ == CCN_TT_DTAG || typ == CCN_TT_DATTR) {
return hunt_for_end(buf, len, valptr, vallen);
}
// case CCN_TT_TAG, CCN_TT_ATTR:
// case DTAG, DATTR:
return -1;
}
static int data2uint(unsigned char *cp, int len)
{
int i, val;
for (i = 0, val = 0; i < len; i++)
if (isdigit(cp[i])) {
val = 10 * val + cp[i] - '0';
}
else {
return -1;
}
return val;
}
struct ccnl_nonce_s *ccnl_nonce_new(struct ccnl_buf_s *that)
{
struct ccnl_nonce_s *n = (struct ccnl_nonce_s *) ccnl_malloc(sizeof(struct ccnl_nonce_s));
n->buf = buf_dup(that);
ccnl_get_timeval(&n->created);
n->next = NULL;
n->prev = NULL;
return n;
}
struct ccnl_nonce_s *ccnl_nonce_remove(struct ccnl_relay_s *ccnl, struct ccnl_nonce_s *nonce)
{
DEBUGMSG(99, "ccnl_nonce_remove: %u:%u:%u:%u\n",
nonce->buf->data[0], nonce->buf->data[1], nonce->buf->data[2], nonce->buf->data[3]);
struct ccnl_nonce_s *next = nonce->next;
DBL_LINKED_LIST_REMOVE(ccnl->nonces, nonce);
free(nonce->buf);
free(nonce);
return next;
}
struct ccnl_buf_s *
ccnl_extract_prefix_nonce_ppkd(unsigned char **data, int *datalen, int *scope,
int *aok, int *min, int *max, struct ccnl_prefix_s **prefix,
struct ccnl_buf_s **nonce, struct ccnl_buf_s **ppkd,
unsigned char **content, int *contlen)
{
unsigned char *start = *data - 2, *cp;
int num, typ, len;
struct ccnl_prefix_s *p;
struct ccnl_buf_s *buf, *n = 0, *pub = 0;
DEBUGMSG(99, "ccnl_extract_prefix\n");
p = (struct ccnl_prefix_s *) ccnl_calloc(1, sizeof(struct ccnl_prefix_s));
if (!p) {
puts("can't get more memory from malloc, dropping ccn msg...");
return NULL;
}
p->comp = (unsigned char **) ccnl_malloc(
CCNL_MAX_NAME_COMP * sizeof(unsigned char **));
p->complen = (int *) ccnl_malloc(CCNL_MAX_NAME_COMP * sizeof(int));
if (!p->comp || !p->complen) {
puts("can't get more memory from malloc, dropping ccn msg...");
goto Bail;
}
while (dehead(data, datalen, &num, &typ) == 0) {
if (num == 0 && typ == 0) {
break; // end
}
if (typ == CCN_TT_DTAG) {
if (num == CCN_DTAG_NAME) {
while (1) {
if (dehead(data, datalen, &num, &typ) != 0) {
goto Bail;
}
if (num == 0 && typ == 0) {
break;
}
if (typ == CCN_TT_DTAG && num == CCN_DTAG_COMPONENT
&& p->compcnt < CCNL_MAX_NAME_COMP) {
if (hunt_for_end(data, datalen, p->comp + p->compcnt,
p->complen + p->compcnt) < 0) {
goto Bail;
}
p->compcnt++;
}
else {
if (consume(typ, num, data, datalen, 0, 0) < 0) {
goto Bail;
}
}
}
continue;
}
if (num == CCN_DTAG_SCOPE || num == CCN_DTAG_NONCE
|| num == CCN_DTAG_MINSUFFCOMP
|| num == CCN_DTAG_MAXSUFFCOMP
|| num == CCN_DTAG_PUBPUBKDIGEST) {
if (hunt_for_end(data, datalen, &cp, &len) < 0) {
goto Bail;
}
if (num == CCN_DTAG_SCOPE && len == 1 && scope) {
*scope = isdigit(*cp) && (*cp < '3') ? *cp - '0' : -1;
}
if (num == CCN_DTAG_ANSWERORIGKIND && aok) {
*aok = data2uint(cp, len);
}
if (num == CCN_DTAG_MINSUFFCOMP && min) {
*min = data2uint(cp, len);
}
if (num == CCN_DTAG_MAXSUFFCOMP && max) {
*max = data2uint(cp, len);
}
if (num == CCN_DTAG_NONCE && !n) {
n = ccnl_buf_new(cp, len);
}
if (num == CCN_DTAG_PUBPUBKDIGEST && !pub) {
pub = ccnl_buf_new(cp, len);
}
if (num == CCN_DTAG_EXCLUDE) {
DEBUGMSG(49, "warning: 'exclude' field ignored\n");
}
else {
continue;
}
}
if (num == CCN_DTAG_CONTENT || num == CCN_DTAG_CONTENTOBJ) {
if (consume(typ, num, data, datalen, content, contlen) < 0) {
goto Bail;
}
continue;
}
}
if (consume(typ, num, data, datalen, 0, 0) < 0) {
goto Bail;
}
}
if (prefix) {
p->comp[p->compcnt] = NULL;
*prefix = p;
}
else {
free_prefix(p);
}
if (nonce) {
*nonce = n;
}
else {
ccnl_free(n);
}
if (ppkd) {
*ppkd = pub;
}
else {
ccnl_free(pub);
}
buf = ccnl_buf_new(start, *data - start);
if (!buf) {
puts("can't get more memory from malloc, dropping ccn msg...");
goto Bail;
}
// carefully rebase ptrs to new buf because of 64bit pointers:
if (content) {
*content = buf->data + (*content - start);
}
for (num = 0; num < p->compcnt; num++) {
p->comp[num] = buf->data + (p->comp[num] - start);
}
return buf;
Bail:
free_prefix(p);
free_2ptr_list(n, pub);
return NULL;
}
// ----------------------------------------------------------------------
// addresses, interfaces and faces
static void ccnl_face_CTS(struct ccnl_relay_s *ccnl, struct ccnl_face_s *f);
int ccnl_addr_cmp(sockunion *s1, sockunion *s2)
{
return (s1->id == s2->id);
}
struct ccnl_face_s *
ccnl_get_face_or_create(struct ccnl_relay_s *ccnl, int ifndx, uint16_t sender_id)
{
struct ccnl_face_s *f;
for (f = ccnl->faces; f; f = f->next) {
if (ifndx == f->ifndx && (f->faceid == sender_id)) {
DEBUGMSG(1, "face found! ifidx=%d sender_id=%d faceid=%d\n", ifndx, sender_id, f->faceid);
ccnl_get_timeval(&f->last_used);
return f;
}
}
f = (struct ccnl_face_s *) ccnl_calloc(1, sizeof(struct ccnl_face_s));
if (!f) {
return NULL;
}
f->faceid = sender_id;
f->ifndx = ifndx;
if (sender_id == RIOT_BROADCAST) {
f->flags |= CCNL_FACE_FLAGS_BROADCAST;
}
if (ifndx >= 0) {
if (ccnl->defaultFaceScheduler)
f->sched = ccnl->defaultFaceScheduler(ccnl,
(void ( *)(void *, void *)) ccnl_face_CTS);
if (ccnl->ifs[ifndx].reflect) {
f->flags |= CCNL_FACE_FLAGS_REFLECT;
}
if (ccnl->ifs[ifndx].fwdalli) {
f->flags |= CCNL_FACE_FLAGS_FWDALLI;
}
}
f->peer.id = sender_id;
#ifdef USE_FRAG
if (ifndx == RIOT_TRANS_IDX) {
// if newly created face, no fragment policy is defined yet
// turning on fragmentation for riot trans dev based faces
int flagval = CCNL_FACE_FLAGS_STATIC;
f->flags = flagval &
(CCNL_FACE_FLAGS_STATIC | CCNL_FACE_FLAGS_REFLECT);
if (f->frag) {
ccnl_frag_destroy(f->frag);
f->frag = NULL;
}
int mtu = ccnl->ifs[f->ifndx].mtu;
f->frag = ccnl_frag_new(CCNL_FRAG_CCNx2013, mtu); //TODO
}
#endif
ccnl_get_timeval(&f->last_used);
DBL_LINKED_LIST_ADD(ccnl->faces, f);
return f;
}
struct ccnl_face_s *
ccnl_face_remove(struct ccnl_relay_s *ccnl, struct ccnl_face_s *f)
{
struct ccnl_face_s *f2;
struct ccnl_interest_s *pit;
struct ccnl_forward_s **ppfwd;
DEBUGMSG(1, "ccnl_face_remove relay=%p face=%p\n", (void *) ccnl, (void *) f);
ccnl_sched_destroy(f->sched);
ccnl_frag_destroy(f->frag);
for (pit = ccnl->pit; pit;) {
struct ccnl_pendint_s **ppend, *pend;
if (pit->from == f) {
pit->from = NULL;
}
for (ppend = &pit->pending; *ppend;) {
if ((*ppend)->face == f) {
pend = *ppend;
*ppend = pend->next;
ccnl_free(pend);
}
else {
ppend = &(*ppend)->next;
}
}
if (pit->pending) {
pit = pit->next;
}
else {
pit = ccnl_interest_remove(ccnl, pit);
}
}
for (ppfwd = &ccnl->fib; *ppfwd;) {
if ((*ppfwd)->face == f) {
struct ccnl_forward_s *pfwd = *ppfwd;
*ppfwd = pfwd->next;
ccnl_forward_remove(ccnl, pfwd);
}
else {
ppfwd = &(*ppfwd)->next;
}
}
while (f->outq) {
struct ccnl_buf_s *tmp = f->outq->next;
ccnl_free(f->outq);
f->outq = tmp;
}
#if ENABLE_DEBUG
ccnl_face_print_stat(f);
#endif
f2 = f->next;
DBL_LINKED_LIST_REMOVE(ccnl->faces, f);
ccnl_free(f);
return f2;
}
#if ENABLE_DEBUG
void ccnl_face_print_stat(struct ccnl_face_s *f)
{
DEBUGMSG(1, "ccnl_face_print_stat: faceid=%d ifndx=%d\n", f->faceid, f->ifndx);
DEBUGMSG(1, " STAT interest send=%d:%d:%d:%d:%d\n", f->stat.send_interest[0],
f->stat.send_interest[1], f->stat.send_interest[2],
f->stat.send_interest[3], f->stat.send_interest[4]);
DEBUGMSG(1, " STAT content send=%d:%d:%d:%d:%d\n", f->stat.send_content[0],
f->stat.send_content[1], f->stat.send_content[2],
f->stat.send_content[3], f->stat.send_content[4]);
DEBUGMSG(1, " STAT interest received=%d\n", f->stat.received_interest);
DEBUGMSG(1, " STAT content received=%d\n", f->stat.received_content);
}
#endif
void ccnl_interface_cleanup(struct ccnl_if_s *i)
{
int j;
DEBUGMSG(99, "ccnl_interface_cleanup\n");
ccnl_sched_destroy(i->sched);
for (j = 0; j < i->qlen; j++) {
struct ccnl_txrequest_s *r = i->queue
+ (i->qfront + j) % CCNL_MAX_IF_QLEN;
ccnl_free(r->buf);
}
}
// ----------------------------------------------------------------------
// face and interface queues, scheduling
void ccnl_interface_CTS(void *aux1, void *aux2)
{
struct ccnl_relay_s *ccnl = (struct ccnl_relay_s *) aux1;
struct ccnl_if_s *ifc = (struct ccnl_if_s *) aux2;
struct ccnl_txrequest_s *r, req;
DEBUGMSG(25, "ccnl_interface_CTS interface=%p, qlen=%d, sched=%p\n",
(void *) ifc, ifc->qlen, (void *) ifc->sched);
if (ifc->qlen <= 0) {
return;
}
r = ifc->queue + ifc->qfront;
memcpy(&req, r, sizeof(req));
ifc->qfront = (ifc->qfront + 1) % CCNL_MAX_IF_QLEN;
ifc->qlen--;
ccnl_ll_TX(ccnl, ifc, &req.dst, req.buf);
ccnl_free(req.buf);
}
void ccnl_interface_enqueue(void (tx_done)(void *, int, int),
struct ccnl_face_s *f, struct ccnl_relay_s *ccnl, struct ccnl_if_s *ifc,
struct ccnl_buf_s *buf, sockunion *dest)
{
struct ccnl_txrequest_s *r;
DEBUGMSG(25, "ccnl_interface_enqueue interface=%p buf=%p (qlen=%d)\n",
(void *) ifc, (void *) buf, ifc->qlen);
if (ifc->qlen >= CCNL_MAX_IF_QLEN) {
DEBUGMSG(2, " DROPPING buf=%p\n", (void *) buf);
ccnl_free(buf);
return;
}
r = ifc->queue + ((ifc->qfront + ifc->qlen) % CCNL_MAX_IF_QLEN);
r->buf = buf;
memcpy(&r->dst, dest, sizeof(sockunion));
r->txdone = tx_done;
r->txdone_face = f;
ifc->qlen++;
ccnl_interface_CTS(ccnl, ifc);
}
struct ccnl_buf_s *
ccnl_face_dequeue(struct ccnl_relay_s *ccnl, struct ccnl_face_s *f)
{
(void) ccnl; /* unused */
struct ccnl_buf_s *pkt;
DEBUGMSG(20, "ccnl_face_dequeue face=%p (id=%d.%d)\n", (void *) f, ccnl->id,
f->faceid);
if (!f->outq) {
return NULL;
}
pkt = f->outq;
f->outq = pkt->next;
if (!pkt->next) {
f->outqend = NULL;
}
pkt->next = NULL;
return pkt;
}
void ccnl_face_CTS_done(void *ptr, int cnt, int len)
{
(void) ptr;
(void) cnt;
(void) len;
DEBUGMSG(99, "ccnl_face_CTS_done face=%p cnt=%d len=%d\n", ptr, cnt, len);
}
void ccnl_face_CTS(struct ccnl_relay_s *ccnl, struct ccnl_face_s *f)
{
DEBUGMSG(99, "ccnl_face_CTS face=%p sched=%p\n", (void *) f,
(void *) f->sched);
if (!f->frag || f->frag->protocol == CCNL_FRAG_NONE) {
struct ccnl_buf_s *buf = ccnl_face_dequeue(ccnl, f);
if (buf)
ccnl_interface_enqueue(ccnl_face_CTS_done, f, ccnl,
ccnl->ifs + f->ifndx, buf, &f->peer);
}
#ifdef USE_FRAG
else {
sockunion dst;
int ifndx = f->ifndx;
buf = ccnl_frag_getnext(f->frag, &ifndx, &dst);
if (!buf) {
buf = ccnl_face_dequeue(ccnl, f);
ccnl_frag_reset(f->frag, buf, f->ifndx, &f->peer);
buf = ccnl_frag_getnext(f->frag, &ifndx, &dst);
}
if (buf) {
ccnl_interface_enqueue(ccnl_face_CTS_done, f,
ccnl, ccnl->ifs + ifndx, buf, &dst);
}
}
#endif
}
int ccnl_face_enqueue(struct ccnl_relay_s *ccnl, struct ccnl_face_s *to,
struct ccnl_buf_s *buf)
{
struct ccnl_buf_s *msg;
DEBUGMSG(20, "ccnl_face_enqueue face=%p (id=%d.%d) buf=%p len=%d\n",
(void *) to, ccnl->id, to->faceid, (void *) buf, buf->datalen);
for (msg = to->outq; msg; msg = msg->next) // already in the queue?
if (buf_equal(msg, buf)) {
DEBUGMSG(31, " not enqueued because already there\n");
ccnl_free(buf);
return -1;
}
buf->next = NULL;
if (to->outqend) {
to->outqend->next = buf;
}
else {
to->outq = buf;
}
to->outqend = buf;
ccnl_face_CTS(ccnl, to);
return 0;
}
// ----------------------------------------------------------------------
// handling of interest messages
int ccnl_nonce_find_or_append(struct ccnl_relay_s *ccnl,
struct ccnl_buf_s *nonce)
{
struct ccnl_nonce_s *n, *last;
int i;
DEBUGMSG(99, "ccnl_nonce_find_or_append: %u:%u:%u:%u\n",
nonce->data[0], nonce->data[1], nonce->data[2], nonce->data[3]);
/* test for noce in nonce cache */
for (n = ccnl->nonces, i = 0; n; n = n->next, i++) {
DEBUGMSG(1, "known: %u:%u:%u:%u\n",
n->buf->data[0], n->buf->data[1], n->buf->data[2], n->buf->data[3]);
if (buf_equal(n->buf, nonce)) {
/* nonce in cache -> known */
return -1;
}
if (n->next) {
last = n->next;
}
}
/* nonce not in local cache, add it */
n = ccnl_nonce_new(nonce);
DBL_LINKED_LIST_ADD(ccnl->nonces, n);
/* nonce chache full? */
if (i >= CCNL_MAX_NONCES) {
/* cache is full, drop oldest nonce: its the last element in the list */
ccnl_nonce_remove(ccnl, last);
}
return 0;
}
struct ccnl_interest_s *
ccnl_interest_new(struct ccnl_relay_s *ccnl, struct ccnl_face_s *from,
struct ccnl_buf_s **pkt, struct ccnl_prefix_s **prefix, int minsuffix,
int maxsuffix, struct ccnl_buf_s **ppkd)
{
struct ccnl_interest_s *i = (struct ccnl_interest_s *) ccnl_calloc(1,
sizeof(struct ccnl_interest_s));
DEBUGMSG(99, "ccnl_new_interest\n");
if (!i) {
puts("can't get more memory from malloc, dropping ccn msg...");
return NULL;
}
i->from = from;
i->prefix = *prefix;
*prefix = 0;
i->pkt = *pkt;
*pkt = 0;
i->ppkd = *ppkd;
*ppkd = 0;
i->minsuffix = minsuffix;
i->maxsuffix = maxsuffix;
ccnl_get_timeval(&i->last_used);
DBL_LINKED_LIST_ADD(ccnl->pit, i);
return i;
}
int ccnl_interest_append_pending(struct ccnl_interest_s *i,
struct ccnl_face_s *from)
{
struct ccnl_pendint_s *pi, *last = NULL;
DEBUGMSG(99, "ccnl_append_pending\n");
for (pi = i->pending; pi; pi = pi->next) { // check whether already listed
if (pi->face == from) {
DEBUGMSG(40, " we found a matching interest, updating time\n");
ccnl_get_timeval(&pi->last_used);
return 0;
}
last = pi;
}
pi = (struct ccnl_pendint_s *) ccnl_calloc(1,
sizeof(struct ccnl_pendint_s));
DEBUGMSG(40, " appending a new pendint entry %p\n", (void *) pi);
if (!pi) {
return -1;
}
pi->face = from;
ccnl_get_timeval(&pi->last_used);
if (last) {
last->next = pi;
}
else {
i->pending = pi;
}
return 0;
}
void ccnl_interest_propagate(struct ccnl_relay_s *ccnl,
struct ccnl_interest_s *i)
{
struct ccnl_forward_s *fwd;
DEBUGMSG(99, "ccnl_interest_propagate\n");
// CONFORM: "A node MUST implement some strategy rule, even if it is only to
// transmit an Interest Message on all listed dest faces in sequence."
// CCNL strategy: we forward on all FWD entries with a prefix match
int forward_cnt = 0;
for (fwd = ccnl->fib; fwd; fwd = fwd->next) {
int rc = ccnl_prefix_cmp(fwd->prefix, NULL, i->prefix, CMP_LONGEST);
DEBUGMSG(40, " ccnl_interest_propagate, rc=%d/%d\n", rc,
fwd->prefix->compcnt);
if (rc < fwd->prefix->compcnt) {
continue;
}
DEBUGMSG(40, " ccnl_interest_propagate, fwd==%p\n", (void *) fwd);
// suppress forwarding to origin of interest, except wireless
if (!i->from || fwd->face != i->from
|| (i->from->flags & CCNL_FACE_FLAGS_REFLECT)) {
i->forwarded_over = fwd;
fwd->face->stat.send_interest[i->retries]++;
ccnl_get_timeval(&i->last_used);
ccnl_face_enqueue(ccnl, fwd->face, buf_dup(i->pkt));
ccnl_get_timeval(&fwd->last_used);
forward_cnt++;
}
}
if (forward_cnt == 0) {
DEBUGMSG(40, " ccnl_interest_propagate: using broadcast face!\n");
ccnl->ifs[RIOT_TRANS_IDX].broadcast_face->stat.send_interest[i->retries]++;
ccnl_get_timeval(&i->last_used);
ccnl_face_enqueue(ccnl, ccnl->ifs[RIOT_TRANS_IDX].broadcast_face, buf_dup(i->pkt));
}
return;
}
struct ccnl_interest_s *
ccnl_interest_remove(struct ccnl_relay_s *ccnl, struct ccnl_interest_s *i)
{
struct ccnl_interest_s *i2;
DEBUGMSG(40, "ccnl_interest_remove %p\n", (void *) i);
while (i->pending) {
struct ccnl_pendint_s *tmp = i->pending->next;
ccnl_free(i->pending);
i->pending = tmp;
}
i2 = i->next;
DBL_LINKED_LIST_REMOVE(ccnl->pit, i);
free_prefix(i->prefix);
free_3ptr_list(i->ppkd, i->pkt, i);
return i2;
}
// ----------------------------------------------------------------------
// handling of content messages
int ccnl_i_prefixof_c(struct ccnl_prefix_s *prefix, struct ccnl_buf_s *ppkd,
int minsuffix, int maxsuffix, struct ccnl_content_s *c)
{
unsigned char *md;
DEBUGMSG(99, "ccnl_i_prefixof_c prefix=%s min=%d max=%d\n",
ccnl_prefix_to_path(prefix), minsuffix, maxsuffix);
// CONFORM: we do prefix match, honour min. and maxsuffix,
// and check the PublisherPublicKeyDigest if present
// NON-CONFORM: "Note that to match a ContentObject must satisfy
// all of the specifications given in the Interest Message."
// >> CCNL does not honour the exclusion filtering
if ((ppkd && !buf_equal(ppkd, c->ppkd))
|| (prefix->compcnt + minsuffix) > (c->name->compcnt + 1)
|| (prefix->compcnt + maxsuffix) < (c->name->compcnt + 1)) {
return 0;
}
md = NULL;
if ((prefix->compcnt - c->name->compcnt) == 1) {
md = compute_ccnx_digest(c->pkt);
}
return ccnl_prefix_cmp(c->name, md, prefix, CMP_MATCH) == prefix->compcnt;
}
struct ccnl_content_s *
ccnl_content_new(struct ccnl_relay_s *ccnl, struct ccnl_buf_s **pkt,
struct ccnl_prefix_s **prefix, struct ccnl_buf_s **ppkd,
unsigned char *content, int contlen)
{
(void) ccnl; /* unused */
struct ccnl_content_s *c;
// DEBUGMSG(99, "ccnl_content_new <%s>\n",
// prefix == NULL ? NULL : ccnl_prefix_to_path(*prefix));
c = (struct ccnl_content_s *) ccnl_calloc(1, sizeof(struct ccnl_content_s));
if (!c) {
return NULL;
}
ccnl_get_timeval(&c->last_used);
c->content = content;
c->contentlen = contlen;
c->pkt = *pkt;
*pkt = NULL;
c->name = *prefix;
*prefix = NULL;
if (ppkd) {
c->ppkd = *ppkd;
*ppkd = NULL;
}
return c;
}
struct ccnl_content_s *
ccnl_content_remove(struct ccnl_relay_s *ccnl, struct ccnl_content_s *c)
{
struct ccnl_content_s *c2;
DEBUGMSG(99, "ccnl_content_remove: %s\n", ccnl_prefix_to_path(c->name));
c2 = c->next;
DBL_LINKED_LIST_REMOVE(ccnl->contents, c);
free_content(c);
ccnl->contentcnt--;
return c2;
}
struct ccnl_content_s *
ccnl_content_add2cache(struct ccnl_relay_s *ccnl, struct ccnl_content_s *c)
{
DEBUGMSG(99, "ccnl_content_add2cache (%d/%d)\n", ccnl->contentcnt,
ccnl->max_cache_entries);
if (ccnl->max_cache_entries <= 0) {
DEBUGMSG(1, " content store disabled...\n");
return NULL;
}
while (ccnl->max_cache_entries <= ccnl->contentcnt) {
DEBUGMSG(1, " remove Least Recently Used content...\n");
struct ccnl_content_s *c2, *lru = NULL;
for (c2 = ccnl->contents; c2; c2 = c2->next) {
DEBUGMSG(1, " '%s' -> %ld:%ld\n", ccnl_prefix_to_path(c2->name), c2->last_used.tv_sec, c2->last_used.tv_usec);
if (!(c2->flags & CCNL_CONTENT_FLAGS_STATIC)
&& ((!lru) || timevaldelta(&c2->last_used, &lru->last_used) < 0)) {
lru = c2;
}
}
if (lru) {
DEBUGMSG(1, " replaced: '%s'\n", ccnl_prefix_to_path(lru->name));
ccnl_content_remove(ccnl, lru);
}
else {
DEBUGMSG(1, " no dynamic content to remove...\n");
break;
}
}
DEBUGMSG(1, " add new content to store: '%s'\n", ccnl_prefix_to_path(c->name));
DBL_LINKED_LIST_ADD(ccnl->contents, c);
ccnl->contentcnt++;
return c;
}
// deliver new content c to all clients with (loosely) matching interest,
// but only one copy per face
// returns: number of forwards
int ccnl_content_serve_pending(struct ccnl_relay_s *ccnl,
struct ccnl_content_s *c,
struct ccnl_face_s *from)
{
struct ccnl_interest_s *i;
struct ccnl_face_s *f;
int cnt = 0;
DEBUGMSG(99, "ccnl_content_serve_pending\n");
for (f = ccnl->faces; f; f = f->next) {
f->flags &= ~CCNL_FACE_FLAGS_SERVED; // reply on a face only once
}
for (i = ccnl->pit; i;) {
struct ccnl_pendint_s *pi;
if (!ccnl_i_prefixof_c(i->prefix, i->ppkd, i->minsuffix, i->maxsuffix,
c)) {
i = i->next;
continue;
}
// CONFORM: "Data MUST only be transmitted in response to
// an Interest that matches the Data."
for (pi = i->pending; pi; pi = pi->next) {
if (pi->face->flags & CCNL_FACE_FLAGS_SERVED) {
continue;
}
if (pi->face == from) {
// the existing pending interest is from the same face
// as the newly arrived content is...no need to send content back
DEBUGMSG(1, " detected looping content, before loop could happen\n");
continue;
}
pi->face->flags |= CCNL_FACE_FLAGS_SERVED;
DEBUGMSG(6, " forwarding content <%s>\n",
ccnl_prefix_to_path(c->name));
pi->face->stat.send_content[c->served_cnt % CCNL_MAX_CONTENT_SERVED_STAT]++;
ccnl_face_enqueue(ccnl, pi->face, buf_dup(c->pkt));
c->served_cnt++;
ccnl_get_timeval(&c->last_used);
cnt++;
}
i = ccnl_interest_remove(ccnl, i);
}
return cnt;
}
/**
* returns an entry from the fib which has a common prefix with p.
* only returns that entry if it fullfils the aggregate threshold!
*/
struct ccnl_forward_s *ccn_forward_find_common_prefix_to_aggregate(struct ccnl_relay_s *ccnl, struct ccnl_prefix_s *p, int *match_len)
{
if (!ccnl->fib) {
DEBUGMSG(999, "ccn_forward_find_common_prefix: fib was empty\n");
return NULL;
}
/* fib as at least one enty */
struct ccnl_forward_s *fwd2 = ccnl->fib;
while (fwd2) {
DEBUGMSG(1, "ccn_forward_find_common_prefix: '%s' vs. '%s'\n", ccnl_prefix_to_path(p), ccnl_prefix_to_path(fwd2->prefix));
*match_len = ccnl_prefix_cmp(fwd2->prefix, 0, p, CMP_LONGEST);
/* check for threshold and never date up a static enty */
if ((ccnl->fib_threshold_aggregate <= *match_len) && !(fwd2->flags & CCNL_FORWARD_FLAGS_STATIC)) {
return fwd2;
}
fwd2 = fwd2->next;
}
return NULL;
}
static struct ccnl_forward_s *ccnl_forward_new(struct ccnl_prefix_s *p, struct ccnl_face_s *f, int threshold_prefix, int flags)
{
struct ccnl_forward_s *fwd = ccnl_calloc(1, sizeof(struct ccnl_forward_s));
fwd->prefix = ccnl_prefix_clone_strip(p, threshold_prefix);
fwd->face = f;
fwd->flags = flags;
return fwd;
}
void ccnl_content_learn_name_route(struct ccnl_relay_s *ccnl, struct ccnl_prefix_s *p, struct ccnl_face_s *f, int threshold_prefix, int flags)
{
/*
* we want to insert a new dynamic route in the fib, let's try to aggregate!
* for aggregation we ask the fib for a prefix match
*/
int match_len;
struct ccnl_forward_s *fwd = ccn_forward_find_common_prefix_to_aggregate(ccnl, p, &match_len);
if (!fwd) {
/* there was no prefix match with the user defined creteria. */
/* create a new fib entry */
fwd = ccnl_forward_new(p, f, threshold_prefix, flags);
DBL_LINKED_LIST_ADD(ccnl->fib, fwd);
DEBUGMSG(999, "ccnl_content_learn_name_route: new route '%s' on face %d learned\n", ccnl_prefix_to_path(fwd->prefix), f->faceid);
}
else {
/* there was a prefix match with the user defined creteria. */
/* if the new entry has shorter prefix */
if (p->compcnt < fwd->prefix->compcnt) {
/* we need to aggregate! */
DBL_LINKED_LIST_REMOVE(ccnl->fib, fwd);
/* create a new fib entry */
fwd = ccnl_forward_new(p, f, (p->compcnt - match_len), flags);
DBL_LINKED_LIST_ADD(ccnl->fib, fwd);
DEBUGMSG(999, "ccnl_content_learn_name_route: route '%s' on face %d replaced\n", ccnl_prefix_to_path(fwd->prefix), f->faceid);
}
else {
/* we don't need to do an update, because we know a shorter prefix already */
}
}
/* refresh fwd entry */
DEBUGMSG(999, "ccnl_content_learn_name_route refresh route '%s' on face %d\n", ccnl_prefix_to_path(fwd->prefix), f->faceid);
ccnl_get_timeval(&fwd->last_used);
}
struct ccnl_forward_s *
ccnl_forward_remove(struct ccnl_relay_s *ccnl, struct ccnl_forward_s *fwd)
{
struct ccnl_forward_s *fwd2;
DEBUGMSG(40, "ccnl_forward_remove %p\n", (void *) fwd);
fwd2 = fwd->next;
DBL_LINKED_LIST_REMOVE(ccnl->fib, fwd);
for (struct ccnl_interest_s *p = ccnl->pit; p; p = p->next) {
if (p->forwarded_over == fwd) {
p->forwarded_over = NULL;
}
}
DEBUGMSG(40, " ccnl_forward_remove next=%p\n", (void *) fwd2);
free_forward(fwd);
return fwd2;
}
bool ccnl_is_timed_out(struct timeval *now, struct timeval *last_used, uint32_t timeout_s, uint32_t timeout_us)
{
timex_t time = timex_set(timeout_s, timeout_us);
timex_normalize(&time);
struct timeval abs_timeout = { last_used->tv_sec + time.seconds, last_used->tv_usec + time.microseconds };
return timevaldelta(now, &abs_timeout) > 0;
}
void ccnl_do_retransmit(void *ptr, void *dummy)
{
(void) dummy; /* unused */
struct ccnl_relay_s *relay = (struct ccnl_relay_s *) ptr;
for (struct ccnl_interest_s *i = relay->pit; i; i = i->next) {
// CONFORM: "Entries in the PIT MUST timeout rather
// than being held indefinitely."
if(i->retries <= CCNL_MAX_INTEREST_RETRANSMIT) {
// CONFORM: "A node MUST retransmit Interest Messages
// periodically for pending PIT entries."
DEBUGMSG(7, " retransmit %d <%s>\n", i->retries,
ccnl_prefix_to_path(i->prefix));
if (i->forwarded_over
&& !(i->forwarded_over->flags & CCNL_FORWARD_FLAGS_STATIC)
&& (i->retries >= CCNL_MAX_INTEREST_OPTIMISTIC)) {
DEBUGMSG(1, " removed dynamic forward %p\n", (void *) i->forwarded_over);
ccnl_forward_remove(relay, i->forwarded_over);
}
i->retries++;
ccnl_interest_propagate(relay, i);
}
}
}
void ccnl_do_nonce_timeout(void *ptr, void *dummy)
{
(void) dummy; /* unused */
struct ccnl_relay_s *relay = (struct ccnl_relay_s *) ptr;
struct timeval now;
ccnl_get_timeval(&now);
//DEBUGMSG(99, "ccnl_do_nonce_timeout: %lu:%lu\n", now.tv_sec, now.tv_usec);
for (struct ccnl_nonce_s *n = relay->nonces; n;) {
if (ccnl_is_timed_out(&now, &n->created, CCNL_NONCE_TIMEOUT_SEC, CCNL_NONCE_TIMEOUT_USEC)) {
n = ccnl_nonce_remove(relay, n);
} else {
n = n->next;
}
}
}
void ccnl_do_ageing(void *ptr, void *dummy)
{
(void) dummy; /* unused */
struct ccnl_relay_s *relay = (struct ccnl_relay_s *) ptr;
struct ccnl_interest_s *i = relay->pit;
struct ccnl_content_s *c = relay->contents;
struct ccnl_face_s *f = relay->faces;
struct timeval now;
ccnl_get_timeval(&now);
//DEBUGMSG(999, "ccnl_do_ageing %ld:%ld\n", now.tv_sec, now.tv_usec);
while (i) {
if (ccnl_is_timed_out(&now, &i->last_used, CCNL_INTEREST_TIMEOUT_SEC,
CCNL_INTEREST_TIMEOUT_USEC)) {
if (i->from->ifndx == RIOT_MSG_IDX) {
/* this interest was requested by an app from this node */
/* inform this app about this problem */
riot_send_nack(i->from->faceid);
}
i = ccnl_interest_remove(relay, i);
}
else {
i = i->next;
}
}
while (c) {
if (ccnl_is_timed_out(&now, &c->last_used, CCNL_CONTENT_TIMEOUT_SEC, CCNL_CONTENT_TIMEOUT_USEC)
&& !(c->flags & CCNL_CONTENT_FLAGS_STATIC)) {
c = ccnl_content_remove(relay, c);
}
else {
c = c->next;
}
}
while (f) {
if (!(f->flags & CCNL_FACE_FLAGS_STATIC)
&& ccnl_is_timed_out(&now, &f->last_used, CCNL_FACE_TIMEOUT_SEC, CCNL_FACE_TIMEOUT_USEC)) {
f = ccnl_face_remove(relay, f);
}
else {
f = f->next;
}
}
struct ccnl_forward_s *fwd = relay->fib;
while (fwd) {
if (!(fwd->flags & CCNL_FORWARD_FLAGS_STATIC)
&& ccnl_is_timed_out(&now, &fwd->last_used, CCNL_FWD_TIMEOUT_SEC, CCNL_FWD_TIMEOUT_USEC)) {
fwd = ccnl_forward_remove(relay, fwd);
}
else {
fwd = fwd->next;
}
}
}
void ccnl_core_cleanup(struct ccnl_relay_s *ccnl)
{
int k;
DEBUGMSG(99, "ccnl_core_cleanup %p\n", (void *) ccnl);
while (ccnl->pit) {
ccnl_interest_remove(ccnl, ccnl->pit);
}
while (ccnl->faces) {
ccnl_face_remove(ccnl, ccnl->faces); // also removes all FWD entries
}
while (ccnl->contents) {
ccnl_content_remove(ccnl, ccnl->contents);
}
while (ccnl->nonces) {
ccnl_nonce_remove(ccnl, ccnl->nonces);
}
for (k = 0; k < ccnl->ifcount; k++) {
ccnl_interface_cleanup(ccnl->ifs + k);
}
}
// ----------------------------------------------------------------------
// the core logic of CCN:
int ccnl_core_RX_i_or_c(struct ccnl_relay_s *relay, struct ccnl_face_s *from,
unsigned char **data, int *datalen)
{
int rc = -1, scope = 3, aok = 3, minsfx = 0, maxsfx = CCNL_MAX_NAME_COMP,
contlen;
struct ccnl_buf_s *buf = 0, *nonce = 0, *ppkd = 0;
struct ccnl_interest_s *i = 0;
struct ccnl_content_s *c = 0;
struct ccnl_prefix_s *p = 0;
unsigned char *content = 0;
DEBUGMSG(1, "ccnl_core_RX_i_or_c: (%d bytes left)\n", *datalen);
buf = ccnl_extract_prefix_nonce_ppkd(data, datalen, &scope, &aok, &minsfx,
&maxsfx, &p, &nonce, &ppkd, &content, &contlen);
if (!buf) {
DEBUGMSG(6, " parsing error or no prefix\n");
goto Done;
}
if (nonce && ccnl_nonce_find_or_append(relay, nonce)) {
DEBUGMSG(6, " dropped because of duplicate nonce\n");
goto Skip;
}
if (buf->data[0] == 0x01 && buf->data[1] == 0xd2) { // interest
DEBUGMSG(1, "ccnl_core_RX_i_or_c: interest=<%s>\n", ccnl_prefix_to_path(p));
from->stat.received_interest++;
if (p->compcnt > 0 && p->comp[0][0] == (unsigned char) 0xc1) {
goto Skip;
}
if (p->compcnt == 4 && !memcmp(p->comp[0], "ccnx", 4)) {
DEBUGMSG(1, "it's a mgnt msg!\n");
rc = ccnl_mgmt(relay, buf, p, from);
DEBUGMSG(1, "mgnt processing done!\n");
goto Done;
}
// CONFORM: Step 1:
if (aok & 0x01) { // honor "answer-from-existing-content-store" flag
for (c = relay->contents; c; c = c->next) {
if (!ccnl_i_prefixof_c(p, ppkd, minsfx, maxsfx, c)) {
continue;
}
// FIXME: should check stale bit in aok here
DEBUGMSG(7, " matching content for interest, content %p\n",
(void *) c);
from->stat.send_content[c->served_cnt % CCNL_MAX_CONTENT_SERVED_STAT]++;
c->served_cnt++;
if (from->ifndx >= 0) {
ccnl_face_enqueue(relay, from, buf_dup(c->pkt));
}
goto Skip;
}
}
// CONFORM: Step 2: check whether interest is already known
for (i = relay->pit; i; i = i->next) {
if (!ccnl_prefix_cmp(i->prefix, NULL, p, CMP_EXACT)
&& i->minsuffix == minsfx && i->maxsuffix == maxsfx
&& ((!ppkd && !i->ppkd) || buf_equal(ppkd, i->ppkd))) {
break;
}
}
if (!i) { // this is a new/unknown I request: create and propagate
i = ccnl_interest_new(relay, from, &buf, &p, minsfx, maxsfx, &ppkd);
if (i) { // CONFORM: Step 3 (and 4)
DEBUGMSG(7, " created new interest entry %p\n", (void *) i);
if (scope > 2) {
ccnl_interest_propagate(relay, i);
}
}
}
else if (scope > 2 && (from->flags & CCNL_FACE_FLAGS_FWDALLI)) {
DEBUGMSG(7, " old interest, nevertheless propagated %p\n",
(void *) i);
ccnl_interest_propagate(relay, i);
}
if (i) { // store the I request, for the incoming face (Step 3)
DEBUGMSG(7, " appending interest entry %p\n", (void *) i);
ccnl_interest_append_pending(i, from);
}
}
else { // content
DEBUGMSG(6, " content=<%s>\n", ccnl_prefix_to_path(p));
from->stat.received_content++;
// CONFORM: Step 1:
for (c = relay->contents; c; c = c->next) {
if (buf_equal(c->pkt, buf)) {
DEBUGMSG(1, "content is dup: skip\n");
goto Skip;
}
}
c = ccnl_content_new(relay, &buf, &p, &ppkd, content, contlen);
if (c) { // CONFORM: Step 2 (and 3)
if (!ccnl_content_serve_pending(relay, c, from)) { // unsolicited content
// CONFORM: "A node MUST NOT forward unsolicited data [...]"
DEBUGMSG(7, " removed because no matching interest\n");
free_content(c);
goto Skip;
}
#if CCNL_DYNAMIC_FIB
else {
/* content has matched an interest, we consider this name as available on this face */
ccnl_content_learn_name_route(relay, c->name, from, relay->fib_threshold_prefix, 0);
}
#endif
if (relay->max_cache_entries != 0) { // it's set to -1 or a limit
DEBUGMSG(7, " adding content to cache\n");
ccnl_content_add2cache(relay, c);
}
else {
DEBUGMSG(7, " content not added to cache\n");
free_content(c);
}
}
}
Skip:
rc = 0;
Done:
free_prefix(p);
free_3ptr_list(buf, nonce, ppkd);
DEBUGMSG(1, "leaving\n");
return rc;
}
int ccnl_core_RX_datagram(struct ccnl_relay_s *relay, struct ccnl_face_s *from,
unsigned char **data, int *datalen)
{
int rc = 0, num = 0, typ = 0;
DEBUGMSG(1, "ccnl_core_RX_datagram: %d bytes from face=%p (id=%d.%d)\n",
*datalen, (void *) from, relay->id, from ? from->faceid : -1);
while (rc >= 0 && *datalen > 0) {
if (dehead(data, datalen, &num, &typ) || typ != CCN_TT_DTAG) {
return -1;
}
switch (num) {
case CCN_DTAG_INTEREST:
/* no break */
case CCN_DTAG_CONTENTOBJ:
rc = ccnl_core_RX_i_or_c(relay, from, data, datalen);
continue;
#ifdef USE_FRAG
case CCNL_DTAG_FRAGMENT2012:
rc = ccnl_frag_RX_frag2012(ccnl_core_RX_datagram, relay, from, data, datalen);
continue;
case CCNL_DTAG_FRAGMENT:
rc = ccnl_frag_RX_CCNx2013(ccnl_core_RX_datagram, relay, from, data, datalen);
continue;
#endif
default:
DEBUGMSG(15, " unknown datagram type %d\n", num);
return -1;
}
}
return rc;
}
void
ccnl_core_RX(struct ccnl_relay_s *relay, int ifndx, unsigned char *data,
int datalen, uint16_t sender_id)
{
struct ccnl_face_s *from;
DEBUGMSG(14, "ccnl_core_RX ifndx=%d, %d bytes\n", ifndx, datalen);
from = ccnl_get_face_or_create(relay, ifndx, sender_id);
if (!from) {
return;
}
DEBUGMSG(1, "ccnl_core_RX: faceid=%d frag=%p\n", from->faceid, (void *) from->frag);
ccnl_core_RX_datagram(relay, from, &data, &datalen);
}
// eof