ccn_traverse.c

Go to the documentation of this file.
00001 /**
00002  * @file ccn_traverse.c
00003  * @brief Support for traversing a branch of the ccn name hierarchy.
00004  * 
00005  * Part of the CCNx C Library.
00006  *
00007  * Copyright (C) 2009 Palo Alto Research Center, Inc.
00008  *
00009  * This library is free software; you can redistribute it and/or modify it
00010  * under the terms of the GNU Lesser General Public License version 2.1
00011  * as published by the Free Software Foundation.
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details. You should have received
00016  * a copy of the GNU Lesser General Public License along with this library;
00017  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00018  * Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #include <assert.h>
00021 #include <stdint.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 
00026 #include <ccn/bloom.h>
00027 #include <ccn/ccn.h>
00028 #include <ccn/charbuf.h>
00029 #include <ccn/uri.h>
00030 
00031 
00032 /************ Candidate API  ******/
00033 
00034 /**
00035  * Private record of the state of traversal
00036  */
00037 struct ccn_traversal {
00038     int magic; /* 68955871 */
00039     long *counter;
00040     unsigned warn;
00041     int flags;
00042     int n_excl;
00043     struct ccn_charbuf **excl; /* Array of n_excl items */
00044     
00045 };
00046 
00047 #define EXCLUDE_LOW 1
00048 #define EXCLUDE_HIGH 2
00049 #define MUST_VERIFY 4
00050 #define LOCAL_SCOPE 8
00051 #define ALLOW_STALE 0x10
00052 
00053 /* Prototypes */
00054 static int namecompare(const void *a, const void *b);
00055 static struct ccn_traversal *get_my_data(struct ccn_closure *selfp);
00056 static void append_Any_filter(struct ccn_charbuf *c);
00057 static int express_my_interest(struct ccn *h,
00058                                struct ccn_closure *selfp,
00059                                struct ccn_charbuf *name);
00060 static struct ccn_closure *split_my_excludes(struct ccn_closure *selfp);
00061 static enum ccn_upcall_res incoming_content(struct ccn_closure *selfp,
00062                                             enum ccn_upcall_kind kind,
00063                                             struct ccn_upcall_info *);
00064 static struct ccn_charbuf *ccn_charbuf_duplicate(struct ccn_charbuf *);
00065 static void answer_passive(struct ccn_charbuf *templ, int allow_stale);
00066 static void local_scope(struct ccn_charbuf *templ);
00067 
00068 /**
00069  * Comparison operator for sorting the excl list with qsort.
00070  * For convenience, the items in the excl array are
00071  * charbufs containing ccnb-encoded Names of one component each.
00072  * (This is not the most efficient representation.)
00073  */
00074 static int /* for qsort */
00075 namecompare(const void *a, const void *b)
00076 {
00077     const struct ccn_charbuf *aa = *(const struct ccn_charbuf **)a;
00078     const struct ccn_charbuf *bb = *(const struct ccn_charbuf **)b;
00079     int ans = ccn_compare_names(aa->buf, aa->length, bb->buf, bb->length);
00080     if (ans == 0)
00081         abort();
00082     return (ans);
00083 }
00084 
00085 static struct ccn_traversal *get_my_data(struct ccn_closure *selfp)
00086 {
00087     struct ccn_traversal *data = selfp->data;
00088     if (data->magic != 68955871) abort();
00089     return(data);
00090 }
00091 
00092 /*
00093  * This upcall gets called for each piece of incoming content that
00094  * matches one of our interests.  We need to issue a new interest that
00095  * excludes another component at the current level, and perhaps also
00096  * and interest to start exploring the next level.  Thus if the matched
00097  * interest is
00098  *   /a/b/c exclude {d,e,f,i,j,k}
00099  * and we get
00100  *   /a/b/c/g/h
00101  * we would issue a new interest
00102  *   /a/b/c exclude {d,e,f,g,i,j,k}
00103  * to continue exploring the current level, plus a simple interest
00104  *   /a/b/c/g
00105  * to start exploring the next level as well.
00106  *
00107  * This does end up fetching each piece of content multiple times, once for
00108  * each level in the name. The repeated requests will be answered from the local
00109  * content store, though, and so should not generate extra network traffic.
00110  * There is a lot of unanswerable interest generated, though.  
00111  *
00112  * To prevent the interests from becoming too huge, we may need to split them.
00113  * Thus if the first new interest above were deemed too large, we could instead
00114  * issue the two interests
00115  *   /a/b/c exclude {d,e,f,g,*}
00116  *   /a/b/c exclude {*,g,i,j,k}
00117  * where * stands for a Bloom filter that excludes anything.  Note the
00118  * repetition of g to ensure that these two interests cover disjoint portions
00119  * of the hierarchy. We need to keep track of the endpoint conditions
00120  * as well as the excluded set in our upcall data.
00121  * When a split happens, we need a new closure to track it, as we do when
00122  * we start exploring a new level.
00123  */
00124 static enum ccn_upcall_res
00125 incoming_content(
00126     struct ccn_closure *selfp,
00127     enum ccn_upcall_kind kind,
00128     struct ccn_upcall_info *info)
00129 {
00130     struct ccn_charbuf *c = NULL;
00131     struct ccn_charbuf *comp = NULL;
00132     struct ccn_charbuf *uri = NULL;
00133     const unsigned char *ccnb = NULL;
00134     size_t ccnb_size = 0;
00135     struct ccn_indexbuf *comps = NULL;
00136     int matched_comps = 0;
00137     int res;
00138     int i;
00139     struct ccn_traversal *data = get_my_data(selfp);
00140     
00141     if (kind == CCN_UPCALL_FINAL) {
00142         for (i = 0; i < data->n_excl; i++)
00143             ccn_charbuf_destroy(&(data->excl[i]));
00144         if (data->excl != NULL)
00145             free(data->excl);
00146         free(data);
00147         free(selfp);
00148         return(0);
00149     }
00150     if (kind == CCN_UPCALL_INTEREST_TIMED_OUT)
00151         return(0);
00152     if (kind == CCN_UPCALL_CONTENT_BAD)
00153         return(0);
00154     if (kind == CCN_UPCALL_CONTENT_UNVERIFIED) {
00155         if ((data->flags & MUST_VERIFY) != 0)
00156             return(CCN_UPCALL_RESULT_VERIFY);
00157     }
00158     if (kind != CCN_UPCALL_CONTENT && kind != CCN_UPCALL_CONTENT_UNVERIFIED) abort();
00159 
00160     ccnb = info->content_ccnb;
00161     ccnb_size = info->pco->offset[CCN_PCO_E];
00162     comps = info->content_comps;
00163     matched_comps = info->pi->prefix_comps;
00164     c = ccn_charbuf_create();
00165     uri = ccn_charbuf_create();
00166         
00167     if (matched_comps + 1 > comps->n) {
00168         ccn_uri_append(c, ccnb, ccnb_size, 1);
00169         fprintf(stderr, "How did this happen?  %s\n", ccn_charbuf_as_string(uri));
00170         exit(1);
00171     }
00172     
00173     data->counter[0]++; /* Tell main that something new came in */
00174 
00175     /* Recover the same prefix as before */
00176     ccn_name_init(c);
00177     ccn_name_append_components(c, ccnb, comps->buf[0], comps->buf[matched_comps]);
00178     
00179     comp = ccn_charbuf_create();
00180     ccn_name_init(comp);
00181     if (matched_comps + 1 == comps->n) {
00182         /* Reconstruct the implicit content digest component */
00183         ccn_digest_ContentObject(ccnb, info->pco);
00184         ccn_name_append(comp, info->pco->digest, info->pco->digest_bytes);
00185     }
00186     else {
00187         ccn_name_append_components(comp, ccnb,
00188                                    comps->buf[matched_comps],
00189                                    comps->buf[matched_comps + 1]);
00190     }
00191     data->excl = realloc(data->excl, (data->n_excl + 1) * sizeof(data->excl[0]));
00192     data->excl[data->n_excl++] = comp;
00193     comp = NULL;
00194     qsort(data->excl, data->n_excl, sizeof(data->excl[0]), &namecompare);
00195     res = express_my_interest(info->h, selfp, c);
00196     if (res == -1) {
00197         struct ccn_closure *high = split_my_excludes(selfp);
00198         if (high == NULL) abort();
00199         express_my_interest(info->h, selfp, c);
00200         express_my_interest(info->h, high, c);
00201     }
00202     /* Explore the next level, if there is one. */
00203     if (matched_comps + 2 < comps->n) {
00204         struct ccn_traversal *newdat = NULL;
00205         struct ccn_closure *cl;
00206         newdat = calloc(1, sizeof(*newdat));
00207         newdat->magic = 68955871;
00208         newdat->warn = 1492;
00209         newdat->counter = data->counter;
00210         newdat->flags = data->flags & ~(EXCLUDE_LOW | EXCLUDE_HIGH);
00211         newdat->n_excl = 0;
00212         newdat->excl = NULL;
00213         cl = calloc(1, sizeof(*cl));
00214         cl->p = &incoming_content;
00215         cl->data = newdat;
00216         ccn_name_init(c);
00217         ccn_name_append_components(c, ccnb,
00218                                    comps->buf[0],
00219                                    comps->buf[matched_comps + 1]);
00220         express_my_interest(info->h, cl, c);
00221     }
00222     else {
00223         res = ccn_uri_append(uri, info->content_ccnb, info->pco->offset[CCN_PCO_E], 1);
00224         if (res < 0)
00225             fprintf(stderr, "*** Error: ccn_traverse line %d res=%d\n", __LINE__, res);
00226         else
00227             printf("%s\n", ccn_charbuf_as_string(uri));
00228     }
00229     ccn_charbuf_destroy(&c);
00230     ccn_charbuf_destroy(&uri);
00231     return(0);
00232 }
00233 
00234 /*
00235  * Construct and send a new interest that uses the exclusion list.
00236  * Return -1 if not sent because of packet size, 0 for success.
00237  */
00238 static int
00239 express_my_interest(struct ccn *h,
00240                     struct ccn_closure *selfp,
00241                     struct ccn_charbuf *name)
00242 {
00243     int ans;
00244     struct ccn_charbuf *templ = NULL;
00245     int i;
00246     struct ccn_traversal *data = get_my_data(selfp);
00247 
00248     templ = ccn_charbuf_create();
00249     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00250     ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00251     ccn_charbuf_append_closer(templ); /* </Name> */
00252     if (data->n_excl != 0) {
00253         ccn_charbuf_append_tt(templ, CCN_DTAG_Exclude, CCN_DTAG);
00254         if ((data->flags & EXCLUDE_LOW) != 0)
00255             append_Any_filter(templ);
00256         for (i = 0; i < data->n_excl; i++) {
00257             struct ccn_charbuf *comp = data->excl[i];
00258             if (comp->length < 4) abort();
00259             ccn_charbuf_append(templ, comp->buf + 1, comp->length - 2);
00260         }
00261         if ((data->flags & EXCLUDE_HIGH) != 0)
00262             append_Any_filter(templ);
00263         ccn_charbuf_append_closer(templ); /* </Exclude> */
00264     }
00265     answer_passive(templ, (data->flags & ALLOW_STALE) != 0);
00266     if ((data->flags & LOCAL_SCOPE) != 0)
00267         local_scope(templ);
00268     ccn_charbuf_append_closer(templ); /* </Interest> */
00269     if (templ->length + name->length > data->warn + 2) {
00270         fprintf(stderr, "*** Interest packet is %d bytes\n", (int)templ->length);
00271         data->warn = data->warn * 8 / 5;
00272     }
00273     if (templ->length + name->length > 1450 && data->n_excl > 3)
00274         ans = -1;
00275     else {
00276         ccn_express_interest(h, name, selfp, templ);
00277         ans = 0;
00278     }
00279     ccn_charbuf_destroy(&templ);
00280     return(ans);
00281 }
00282 
00283 /*
00284  * Build a new closure to handle the high half of the excludes, and modify the
00285  * old closure to handle the low half.
00286  */
00287 static struct ccn_closure *
00288 split_my_excludes(struct ccn_closure *selfp)
00289 {
00290     int i;
00291     int m;
00292     struct ccn_traversal *newdat = NULL;
00293     struct ccn_closure *cl;
00294     struct ccn_traversal *data = get_my_data(selfp);
00295     
00296     if (data->n_excl < 3)
00297         return NULL;
00298     m = data->n_excl / 2;
00299     newdat = calloc(1, sizeof(*newdat));
00300     newdat->magic = 68955871;
00301     newdat->warn = 1492;
00302     newdat->counter = data->counter;
00303     newdat->n_excl = data->n_excl - m;
00304     newdat->excl = calloc(newdat->n_excl, sizeof(newdat->excl[0]));
00305     if (newdat->excl == NULL) {
00306         free(newdat);
00307         return(NULL);
00308     }
00309     newdat->excl[0] = ccn_charbuf_duplicate(data->excl[m]);
00310     newdat->flags = data->flags | EXCLUDE_LOW;
00311     for (i = 1; i < newdat->n_excl; i++) {
00312         newdat->excl[i] = data->excl[m + i];
00313         data->excl[m + i] = NULL;
00314     }
00315     data->n_excl = m + 1;
00316     data->flags |= EXCLUDE_HIGH;
00317     cl = calloc(1, sizeof(*cl));
00318     cl->p = &incoming_content;
00319     cl->data = newdat;
00320     return(cl);
00321 }
00322 
00323 /**
00324  * Append an Any filter, useful for excluding
00325  * everything between two 'fenceposts' in an Exclude construct.
00326  */
00327 static void
00328 append_Any_filter(struct ccn_charbuf *c)
00329 {
00330     ccn_charbuf_append_tt(c, CCN_DTAG_Any, CCN_DTAG);
00331     ccn_charbuf_append_closer(c);
00332 }
00333 
00334 static struct ccn_charbuf *
00335 ccn_charbuf_duplicate(struct ccn_charbuf *c)
00336 {
00337     struct ccn_charbuf *ans = ccn_charbuf_create();
00338     ccn_charbuf_append(ans, c->buf, c->length);
00339     return(ans);
00340 }
00341 
00342 /*
00343  * Append AnswerOriginKind element to partially constructed Interest,
00344  * requesting to not generate new content.
00345  */
00346 static void
00347 answer_passive(struct ccn_charbuf *templ, int allow_stale)
00348 {
00349     int aok = CCN_AOK_CS;
00350     if (allow_stale)
00351         aok |= CCN_AOK_STALE;
00352     ccnb_tagged_putf(templ, CCN_DTAG_AnswerOriginKind, "%d", aok);
00353 }
00354 
00355 /*
00356  * Append Scope=0 to partially constructed Interest, meaning
00357  * to address only the local ccnd.
00358  */
00359 static void
00360 local_scope(struct ccn_charbuf *templ)
00361 {
00362     ccn_charbuf_append_tt(templ, CCN_DTAG_Scope, CCN_DTAG);
00363     ccn_charbuf_append_tt(templ, 1, CCN_UDATA);
00364     ccn_charbuf_append(templ, "0", 1);
00365     ccn_charbuf_append_closer(templ); /* </Scope> */
00366 }
00367 
00368 /**
00369  * Temporary driver - exits when done!
00370  */
00371 
00372 void
00373 ccn_dump_names(struct ccn *h, struct ccn_charbuf *name_prefix, int local_scope, int allow_stale)
00374 {
00375     long *counter;
00376     int i;
00377     long n;
00378     int res;
00379     struct ccn_traversal *data = NULL;
00380     struct ccn_closure *cl = NULL;
00381     
00382     counter = calloc(1, sizeof(*counter));
00383     data = calloc(1, sizeof(*data));
00384     data->magic = 68955871;
00385     data->warn = 1492;
00386     data->flags = 0;
00387     data->counter = counter;
00388     if (local_scope)
00389         data->flags |= LOCAL_SCOPE;
00390     if (allow_stale)
00391         data->flags |= ALLOW_STALE;
00392     
00393     cl = calloc(1, sizeof(*cl));
00394     cl->p = &incoming_content;
00395     cl->data = data;
00396     
00397     express_my_interest(h, cl, name_prefix);
00398     cl = NULL;
00399     data = NULL;
00400     for (i = 0;; i++) {
00401         n = *counter;
00402         res = ccn_run(h, 1000); /* stop if we run dry for 1 sec */
00403         fflush(stdout);
00404         if (*counter == n || res < 0)
00405             break;
00406     }
00407     exit(0);
00408 }

Generated on Thu Feb 16 00:44:00 2012 for Content-Centric Networking in C by  doxygen 1.5.6