00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00035 #define GOT_HERE() ((void)(__LINE__))
00036
00037 struct excludestuff;
00038
00039 struct ooodata {
00040 struct ccn_closure closure;
00041 unsigned char *raw_data;
00042 size_t raw_data_size;
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);
00240
00241 ccn_charbuf_append_tt(templ, CCN_DTAG_MaxSuffixComponents, CCN_DTAG);
00242 ccnb_append_number(templ, 1);
00243 ccn_charbuf_append_closer(templ);
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);
00248 }
00249 ccn_charbuf_append_closer(templ);
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
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
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
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
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
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
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
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
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
00619 ask_more(mydata, 0);
00620
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
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 }