ccncatchunks2.c

Go to the documentation of this file.
00001 /**
00002  * @file ccncatchunks2.c
00003  * Reads stuff written by ccnsendchunks, writes to stdout.
00004  *
00005  * A CCNx command-line utility.
00006  *
00007  * Copyright (C) 2008-2010 Palo Alto Research Center, Inc.
00008  *
00009  * This work is free software; you can redistribute it and/or modify it under
00010  * the terms of the GNU General Public License version 2 as published by the
00011  * Free Software Foundation.
00012  * This work is distributed in the hope that it will be useful, but WITHOUT ANY
00013  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
00015  * for more details. You should have received a copy of the GNU General Public
00016  * License along with this program; if not, write to the
00017  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 #include <sys/time.h>
00021 #include <assert.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <time.h>
00026 #include <unistd.h>
00027 
00028 #include <ccn/ccn.h>
00029 #include <ccn/charbuf.h>
00030 #include <ccn/schedule.h>
00031 #include <ccn/uri.h>
00032 
00033 #define PIPELIMIT (1U << 7)
00034 //#define GOT_HERE() fprintf(stderr, "LINE %d\n", __LINE__)
00035 #define GOT_HERE() ((void)(__LINE__))
00036 
00037 struct excludestuff;
00038 
00039 struct ooodata {
00040     struct ccn_closure closure;     /* closure per slot */
00041     unsigned char *raw_data;        /* content that has arrived out-of-order */
00042     size_t raw_data_size;           /* its size (plus 1) in bytes */
00043 };
00044 
00045 struct mydata {
00046     struct ccn *h;
00047     int allow_stale;
00048     int use_decimal;
00049     int dummy;
00050     unsigned ooo_base;
00051     unsigned ooo_count;
00052     unsigned curwindow;
00053     unsigned maxwindow;
00054     unsigned sendtime;
00055     unsigned sendtime_slot;
00056     unsigned rtt;
00057     unsigned rtte;
00058     unsigned backoff;
00059     unsigned finalslot;
00060     struct ccn_charbuf *name;
00061     struct ccn_charbuf *templ;
00062     struct excludestuff *excl;
00063     struct ccn_schedule *sched;
00064     struct ccn_scheduled_event *report;
00065     struct ccn_scheduled_event *holefiller;
00066     intmax_t interests_sent;
00067     intmax_t pkts_recvd;
00068     intmax_t co_bytes_recvd;
00069     intmax_t delivered;
00070     intmax_t delivered_bytes;
00071     intmax_t junk;
00072     intmax_t holes;
00073     intmax_t timeouts;
00074     intmax_t dups;
00075     intmax_t lastcheck;
00076     intmax_t unverified;
00077     struct timeval start_tv;
00078     struct timeval stop_tv;
00079     struct ooodata ooo[PIPELIMIT];
00080 };
00081 
00082 static int fill_holes(struct ccn_schedule *sched, void *clienth, 
00083                       struct ccn_scheduled_event *ev, int flags);
00084 
00085 static FILE* logstream = NULL;
00086 
00087 static void
00088 usage(const char *progname)
00089 {
00090     fprintf(stderr,
00091             "%s [-a] [-p n] ccnx:/a/b\n"
00092             "   Reads stuff written by ccnsendchunks under"
00093             " the given uri and writes to stdout\n"
00094             "   -a - allow stale data\n"
00095             "   -d - discard data instead of writing (also skips verification)\n"
00096             "   -p n - use up to n pipeline slots\n"
00097             "   -s - use new-style segmentation markers\n",
00098             progname);
00099     exit(1);
00100 }
00101 
00102 static void
00103 mygettime(const struct ccn_gettime *self, struct ccn_timeval *result)
00104 {
00105     struct timeval now = {0};
00106     gettimeofday(&now, 0);
00107     result->s = now.tv_sec;
00108     result->micros = now.tv_usec;
00109 }
00110 
00111 static struct ccn_gettime myticker = {
00112     "timer",
00113     &mygettime,
00114     1000000,
00115     NULL
00116 };
00117 
00118 static void
00119 update_rtt(struct mydata *md, int incoming, unsigned slot)
00120 {
00121     struct timeval now = {0};
00122     unsigned t, delta, rtte;
00123     
00124     if (!incoming && md->sendtime_slot == ~0)
00125         md->sendtime_slot = slot;
00126     if (slot != md->sendtime_slot)
00127         return;
00128     gettimeofday(&now, 0);
00129     t = ((unsigned)(now.tv_sec) * 1000000) + (unsigned)(now.tv_usec);
00130     if (incoming) {
00131         delta = t - md->sendtime;
00132         md->rtt = delta;
00133         if (delta <= 30000000) {
00134             rtte = md->rtte;
00135             if (delta > rtte)
00136                 rtte = rtte + (rtte >> 3);
00137             else
00138                 rtte = rtte - (rtte >> 7);
00139             if (rtte < 127)
00140                 rtte = delta;
00141             md->rtte = rtte;
00142         }
00143         if (md->holefiller == NULL)
00144             md->holefiller = ccn_schedule_event(md->sched, 10000, &fill_holes, NULL, 0);
00145         md->sendtime_slot = ~0;
00146         if (logstream)
00147             fprintf(logstream,
00148                     "%ld.%06u ccncatchunks2: "
00149                     "%jd isent, %jd recvd, %jd junk, %jd holes, %jd t/o, %jd unvrf, "
00150                     "%u curwin, %u rtt, %u rtte\n",
00151                     (long)now.tv_sec,
00152                     (unsigned)now.tv_usec,
00153                     md->interests_sent,
00154                     md->pkts_recvd,
00155                     md->junk,
00156                     md->holes,
00157                     md->timeouts,
00158                     md->unverified,
00159                     md->curwindow,
00160                     md->rtt,
00161                     md->rtte
00162                     );
00163     }
00164     else
00165         md->sendtime = t;
00166 }
00167 
00168 static int
00169 reporter(struct ccn_schedule *sched, void *clienth, 
00170          struct ccn_scheduled_event *ev, int flags)
00171 {
00172     struct timeval now = {0};
00173     struct mydata *md = clienth;
00174     gettimeofday(&now, 0);
00175     fflush(stdout);
00176     fprintf(stderr,
00177             "%ld.%06u ccncatchunks2[%d]: "
00178             "%jd isent, %jd recvd, %jd junk, %jd holes, %jd t/o, %jd unvrf, "
00179             "%u curwin, %u rtt, %u rtte\n",
00180             (long)now.tv_sec,
00181             (unsigned)now.tv_usec,
00182             (int)getpid(),
00183             md->interests_sent,
00184             md->pkts_recvd,
00185             md->junk,
00186             md->holes,
00187             md->timeouts,
00188             md->unverified,
00189             md->curwindow,
00190             md->rtt,
00191             md->rtte
00192             );
00193     if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00194         md->report = NULL;
00195         return(0);
00196     }
00197     return(3000000);
00198 }
00199 
00200 void
00201 print_summary(struct mydata *md)
00202 {
00203     const char *expid;
00204     const char *dlm = " ";
00205     double elapsed = 0.0;
00206     intmax_t delivered_bytes;
00207     double rate = 0.0;
00208     
00209     expid = getenv("CCN_EXPERIMENT_ID");
00210     if (expid == NULL)
00211         expid = dlm = "";
00212     gettimeofday(&md->stop_tv, 0);
00213     elapsed = (double)(long)(md->stop_tv.tv_sec - md->start_tv.tv_sec);
00214     elapsed += ((int)md->stop_tv.tv_usec - (int)md->start_tv.tv_usec)/1000000.0;
00215     delivered_bytes = md->delivered_bytes;
00216     if (elapsed > 0.00001)
00217         rate = delivered_bytes/elapsed;
00218     fprintf(stderr,
00219             "%ld.%06u ccncatchunks2[%d]: %s%s"
00220             "%jd bytes transferred in %.6f seconds (%.0f bytes/sec)"
00221             "\n",
00222             (long)md->stop_tv.tv_sec,
00223             (unsigned)md->stop_tv.tv_usec,
00224             (int)getpid(),
00225             expid,
00226             dlm,
00227             delivered_bytes,
00228             elapsed,
00229             rate
00230             );
00231 }
00232 
00233 struct ccn_charbuf *
00234 make_template(struct mydata *md)
00235 {
00236     struct ccn_charbuf *templ = ccn_charbuf_create();
00237     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00238     ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00239     ccn_charbuf_append_closer(templ); /* </Name> */
00240     // XXX - use pubid if possible
00241     ccn_charbuf_append_tt(templ, CCN_DTAG_MaxSuffixComponents, CCN_DTAG);
00242     ccnb_append_number(templ, 1);
00243     ccn_charbuf_append_closer(templ); /* </MaxSuffixComponents> */
00244     if (md->allow_stale) {
00245         ccn_charbuf_append_tt(templ, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
00246         ccnb_append_number(templ, CCN_AOK_DEFAULT | CCN_AOK_STALE);
00247         ccn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
00248     }
00249     ccn_charbuf_append_closer(templ); /* </Interest> */
00250     return(templ);
00251 }
00252 
00253 static struct ccn_charbuf *
00254 sequenced_name(struct mydata *md, uintmax_t seq)
00255 {
00256     struct ccn_charbuf *name = NULL;
00257     struct ccn_charbuf *temp = NULL;
00258     
00259     name = ccn_charbuf_create();
00260     ccn_charbuf_append(name, md->name->buf, md->name->length);
00261     if (md->use_decimal) {
00262         temp = ccn_charbuf_create();
00263         ccn_charbuf_putf(temp, "%ju", seq);
00264         ccn_name_append(name, temp->buf, temp->length);
00265         ccn_charbuf_destroy(&temp);
00266     }
00267     else
00268         ccn_name_append_numeric(name, CCN_MARKER_SEQNUM, seq);
00269     return(name);
00270 }
00271 
00272 static void
00273 ask_more(struct mydata *md, uintmax_t seq)
00274 {
00275     struct ccn_charbuf *name = NULL;
00276     struct ccn_charbuf *templ = NULL;
00277     int res;
00278     unsigned slot;
00279     struct ccn_closure *cl = NULL;
00280     
00281     slot = seq % PIPELIMIT;
00282     cl = &md->ooo[slot].closure;
00283     if (cl->intdata == -1)
00284         cl->intdata = seq;
00285     assert(cl->intdata == seq);
00286     assert(md->ooo[slot].raw_data_size == 0);
00287     name = sequenced_name(md, seq);
00288     templ = make_template(md);
00289     update_rtt(md, 0, slot);
00290     res = ccn_express_interest(md->h, name, cl, templ);
00291     if (res < 0) abort();
00292     md->interests_sent++;
00293     ccn_charbuf_destroy(&templ);
00294     ccn_charbuf_destroy(&name);
00295     if (seq == md->delivered + md->ooo_count)
00296         md->ooo_count++;
00297     assert(seq >= md->delivered);
00298     assert(seq < md->delivered + md->ooo_count);
00299     assert(md->ooo_count < PIPELIMIT);
00300 }
00301 
00302 static enum ccn_upcall_res
00303 hole_filled(struct ccn_closure *selfp,
00304     enum ccn_upcall_kind kind,
00305     struct ccn_upcall_info *info)
00306 {
00307     if (kind == CCN_UPCALL_FINAL)
00308         free(selfp);
00309     return(CCN_UPCALL_RESULT_OK);
00310 }
00311 
00312 static int
00313 fill_holes(struct ccn_schedule *sched, void *clienth, 
00314          struct ccn_scheduled_event *ev, int flags)
00315 {
00316     struct mydata *md = clienth;
00317     struct ccn_charbuf *name = NULL;
00318     struct ccn_charbuf *templ = NULL;
00319     struct ccn_closure *cl = NULL;
00320     unsigned backoff;
00321     int delay;
00322     
00323     if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00324         md->holefiller = NULL;
00325         return(0);
00326     }
00327     backoff = md->backoff;
00328     if (md->delivered == md->lastcheck && md->ooo_count > 0) {
00329         if (backoff == 0) {
00330             md->holes++;
00331             fprintf(stderr, "*** Hole at %jd\n", md->delivered);
00332             reporter(sched, md, NULL, 0);
00333             md->curwindow = 1;
00334             cl = calloc(1, sizeof(*cl));
00335             cl->p = &hole_filled;
00336             name = sequenced_name(md, md->delivered);
00337             templ = make_template(md);
00338             ccn_express_interest(md->h, name, cl, templ);
00339             md->interests_sent++;
00340             ccn_charbuf_destroy(&templ);
00341             ccn_charbuf_destroy(&name);
00342         }
00343         if ((6000000 >> backoff) > md->rtte)
00344             backoff++;
00345     }
00346     else {
00347         md->lastcheck = md->delivered;
00348         backoff = 0;
00349     }
00350     md->backoff = backoff;
00351     delay = (md->rtte << backoff);
00352     if (delay < 10000)
00353         delay = 10000;
00354     return(delay);
00355 }
00356 
00357 static int
00358 is_final(struct ccn_upcall_info *info)
00359 {
00360         // XXX The test below should get refactored into the library
00361     const unsigned char *ccnb;
00362     size_t ccnb_size;
00363     ccnb = info->content_ccnb;
00364     if (ccnb == NULL || info->pco == NULL)
00365         return(0);
00366     ccnb_size = info->pco->offset[CCN_PCO_E];
00367     if (info->pco->offset[CCN_PCO_B_FinalBlockID] !=
00368         info->pco->offset[CCN_PCO_E_FinalBlockID]) {
00369         const unsigned char *finalid = NULL;
00370         size_t finalid_size = 0;
00371         const unsigned char *nameid = NULL;
00372         size_t nameid_size = 0;
00373         struct ccn_indexbuf *cc = info->content_comps;
00374         ccn_ref_tagged_BLOB(CCN_DTAG_FinalBlockID, ccnb,
00375                             info->pco->offset[CCN_PCO_B_FinalBlockID],
00376                             info->pco->offset[CCN_PCO_E_FinalBlockID],
00377                             &finalid,
00378                             &finalid_size);
00379         if (cc->n < 2) abort();
00380         ccn_ref_tagged_BLOB(CCN_DTAG_Component, ccnb,
00381                             cc->buf[cc->n - 2],
00382                             cc->buf[cc->n - 1],
00383                             &nameid,
00384                             &nameid_size);
00385         if (finalid_size == nameid_size &&
00386               0 == memcmp(finalid, nameid, nameid_size))
00387             return(1);
00388     }
00389     return(0);
00390 }
00391 
00392 enum ccn_upcall_res
00393 incoming_content(struct ccn_closure *selfp,
00394                  enum ccn_upcall_kind kind,
00395                  struct ccn_upcall_info *info)
00396 {
00397     const unsigned char *ccnb = NULL;
00398     size_t ccnb_size = 0;
00399     const unsigned char *data = NULL;
00400     size_t data_size = 0;
00401     size_t written;
00402     int res;
00403     struct mydata *md = selfp->data;
00404     unsigned slot;
00405     
00406     if (kind == CCN_UPCALL_FINAL) {
00407         selfp->intdata = -1;
00408         return(CCN_UPCALL_RESULT_OK);
00409     }
00410 GOT_HERE();
00411     if (kind == CCN_UPCALL_INTEREST_TIMED_OUT) {
00412         md->timeouts++;
00413         if (selfp->refcount > 1 || selfp->intdata == -1)
00414             return(CCN_UPCALL_RESULT_OK);
00415         md->interests_sent++;
00416         md->curwindow = 1;
00417         // XXX - may need to reseed bloom filter
00418         return(CCN_UPCALL_RESULT_REEXPRESS);
00419     }
00420 GOT_HERE();
00421     assert(md != NULL);
00422     switch (kind) {
00423         case CCN_UPCALL_CONTENT:
00424              break;
00425         case CCN_UPCALL_CONTENT_UNVERIFIED:
00426              if (md->pkts_recvd == 0)
00427             return(CCN_UPCALL_RESULT_VERIFY);
00428             md->unverified++;
00429             break;
00430 #if (CCN_API_VERSION >= 4004)
00431         case CCN_UPCALL_CONTENT_RAW:
00432             break;
00433         case CCN_UPCALL_CONTENT_KEYMISSING:
00434             if (md->pkts_recvd == 0)
00435             return(CCN_UPCALL_RESULT_FETCHKEY);
00436             md->unverified++;
00437             break;
00438 #endif
00439         default:
00440             return(CCN_UPCALL_RESULT_ERR);
00441     }
00442     md->pkts_recvd++;
00443     if (selfp->intdata == -1) {
00444         /* Outside the window we care about. Toss it. */
00445         md->dups++;
00446         return(CCN_UPCALL_RESULT_OK);
00447     }
00448     ccnb = info->content_ccnb;
00449     ccnb_size = info->pco->offset[CCN_PCO_E];
00450     res = ccn_content_get_value(ccnb, ccnb_size, info->pco, &data, &data_size);
00451     if (res < 0) abort();
00452 GOT_HERE();
00453     /* OK, we will accept this block. */
00454     md->co_bytes_recvd += data_size;
00455     slot = ((uintptr_t)selfp->intdata) % PIPELIMIT;
00456     assert(selfp == &md->ooo[slot].closure);
00457     if (is_final(info)) {
00458         GOT_HERE();
00459         md->finalslot = slot;
00460     }
00461     if (slot != md->ooo_base || md->ooo_count == 0) {
00462         /* out-of-order data, save for later */
00463         struct ooodata *ooo = &md->ooo[slot];
00464         if (ooo->raw_data_size == 0) {
00465 GOT_HERE();
00466             update_rtt(md, 1, slot);
00467             ooo->raw_data = malloc(data_size);
00468             memcpy(ooo->raw_data, data, data_size);
00469             ooo->raw_data_size = data_size + 1;
00470         }
00471         else
00472             md->dups++;
00473         if (md->curwindow > 1)
00474             md->curwindow--;
00475     }
00476     else {
00477         assert(md->ooo[slot].raw_data_size == 0);
00478         update_rtt(md, 1, slot);
00479         md->ooo[slot].closure.intdata = -1;
00480         md->delivered++;
00481         md->delivered_bytes += data_size;
00482         written = md->dummy;
00483         if (! written)
00484             written = fwrite(data, data_size, 1, stdout);
00485         if (written != 1)
00486             exit(1);
00487         /* Check for EOF */
00488         if (slot == md->finalslot) {
00489             GOT_HERE();
00490             ccn_schedule_destroy(&md->sched);
00491             print_summary(md);
00492             exit(0);
00493         }
00494         md->ooo_count--;
00495         slot = (slot + 1) % PIPELIMIT;
00496         if (md->curwindow < md->maxwindow)
00497             md->curwindow++;
00498         while (md->ooo_count > 0 && md->ooo[slot].raw_data_size != 0) {
00499             struct ooodata *ooo = &md->ooo[slot];
00500             md->delivered++;
00501             md->delivered_bytes += (ooo->raw_data_size - 1);
00502             written = md->dummy;
00503             if (! written)
00504                 written = fwrite(ooo->raw_data, ooo->raw_data_size - 1, 1, stdout);
00505             if (written != 1)
00506                 exit(1);
00507             /* Check for EOF */
00508             if (slot == md->finalslot) {
00509                 GOT_HERE();
00510                 ccn_schedule_destroy(&md->sched);
00511                 print_summary(md);
00512                 exit(0);
00513             }
00514             free(ooo->raw_data);
00515             ooo->raw_data = NULL;
00516             ooo->raw_data_size = 0;
00517             slot = (slot + 1) % PIPELIMIT;
00518             md->ooo_count--;
00519         }
00520         md->ooo_base = slot;
00521     }
00522     
00523     /* Ask for the next one or two */
00524     if (md->ooo_count < md->curwindow)
00525         ask_more(md, md->delivered + md->ooo_count);
00526     if (md->ooo_count < md->curwindow)
00527         ask_more(md, md->delivered + md->ooo_count);
00528     
00529     return(CCN_UPCALL_RESULT_OK);
00530 }
00531 
00532 int
00533 main(int argc, char **argv)
00534 {
00535     struct ccn *ccn = NULL;
00536     struct ccn_charbuf *name = NULL;
00537     struct ccn_closure *incoming = NULL;
00538     const char *arg = NULL;
00539     int res;
00540     int micros;
00541     int opt;
00542     struct mydata *mydata;
00543     int allow_stale = 0;
00544     int use_decimal = 1;
00545     int i;
00546     unsigned maxwindow = PIPELIMIT-1;
00547     int dummy = 0;
00548     
00549     if (maxwindow > 31)
00550         maxwindow = 31;
00551     
00552     while ((opt = getopt(argc, argv, "hadp:s")) != -1) {
00553         switch (opt) {
00554             case 'a':
00555                 allow_stale = 1;
00556                 break;
00557             case 'd':
00558                 dummy = 1;
00559                 break;
00560             case 'p':
00561                 res = atoi(optarg);
00562                 if (1 <= res && res < PIPELIMIT)
00563                     maxwindow = res;
00564                 else
00565                     usage(argv[0]);
00566                 break;
00567             case 's':
00568                 use_decimal = 0;
00569                 break;
00570             case 'h':
00571             default:
00572                 usage(argv[0]);
00573         }
00574     }
00575     arg = argv[optind];
00576     if (arg == NULL)
00577         usage(argv[0]);
00578     name = ccn_charbuf_create();
00579     res = ccn_name_from_uri(name, arg);
00580     if (res < 0) {
00581         fprintf(stderr, "%s: bad ccn URI: %s\n", argv[0], arg);
00582         exit(1);
00583     }
00584     if (argv[optind + 1] != NULL)
00585         fprintf(stderr, "%s warning: extra arguments ignored\n", argv[0]);
00586     ccn = ccn_create();
00587     if (ccn_connect(ccn, NULL) == -1) {
00588         perror("Could not connect to ccnd");
00589         exit(1);
00590     }
00591 #if (CCN_API_VERSION >= 4004)
00592     if (dummy)
00593         ccn_defer_verification(ccn, 1);
00594 #endif
00595     mydata = calloc(1, sizeof(*mydata));
00596     mydata->h = ccn;
00597     mydata->name = name;
00598     mydata->allow_stale = allow_stale;
00599     mydata->use_decimal = use_decimal;
00600     mydata->excl = NULL;
00601     mydata->sched = ccn_schedule_create(mydata, &myticker);
00602     mydata->report = ccn_schedule_event(mydata->sched, 0, &reporter, NULL, 0);
00603     mydata->holefiller = NULL;
00604     mydata->maxwindow = maxwindow;
00605     mydata->finalslot = ~0;
00606     mydata->dummy = dummy;
00607     for (i = 0; i < PIPELIMIT; i++) {
00608         incoming = &mydata->ooo[i].closure;
00609         incoming->p = &incoming_content;
00610         incoming->data = mydata;
00611         incoming->intdata = -1;
00612     }
00613     mydata->ooo_base = 0;
00614     mydata->ooo_count = 0;
00615     mydata->curwindow = 1;
00616     gettimeofday(&mydata->start_tv, 0);
00617     logstream = NULL;
00618     // logstream = fopen("xxxxxxxxxxxxxxlogstream" + (unsigned)getpid()%10, "wb"); 
00619     ask_more(mydata, 0);
00620     /* Run a little while to see if there is anything there */
00621     res = ccn_run(ccn, 500);
00622     if (mydata->delivered == 0) {
00623         fprintf(stderr, "%s: not found: %s\n", argv[0], arg);
00624         exit(1);
00625     }
00626     /* We got something, run until end of data or somebody kills us */
00627     while (res >= 0) {
00628         micros = ccn_schedule_run(mydata->sched);
00629         if (micros < 0)
00630             micros = 10000000;
00631         res = ccn_run(ccn, micros / 1000);
00632     }
00633     ccn_schedule_destroy(&mydata->sched);
00634     print_summary(mydata);
00635     ccn_destroy(&ccn);
00636     exit(res < 0);
00637 }

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