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 unsigned ooo_base;
00050 unsigned ooo_count;
00051 unsigned curwindow;
00052 unsigned maxwindow;
00053 unsigned sendtime;
00054 unsigned sendtime_slot;
00055 unsigned rtt;
00056 unsigned rtte;
00057 unsigned backoff;
00058 unsigned finalslot;
00059 struct ccn_charbuf *name;
00060 struct ccn_charbuf *templ;
00061 struct excludestuff *excl;
00062 struct ccn_schedule *sched;
00063 struct ccn_scheduled_event *report;
00064 struct ccn_scheduled_event *holefiller;
00065 intmax_t interests_sent;
00066 intmax_t pkts_recvd;
00067 intmax_t co_bytes_recvd;
00068 intmax_t delivered;
00069 intmax_t delivered_bytes;
00070 intmax_t junk;
00071 intmax_t holes;
00072 intmax_t timeouts;
00073 intmax_t dups;
00074 intmax_t lastcheck;
00075 intmax_t unverified;
00076 struct timeval start_tv;
00077 struct timeval stop_tv;
00078 struct ooodata ooo[PIPELIMIT];
00079 };
00080
00081 static int fill_holes(struct ccn_schedule *sched, void *clienth,
00082 struct ccn_scheduled_event *ev, int flags);
00083
00084 static FILE* logstream = NULL;
00085
00086 static void
00087 usage(const char *progname)
00088 {
00089 fprintf(stderr,
00090 "%s [-a] [-p n] ccnx:/a/b\n"
00091 " Reads stuff written by ccnsendchunks under"
00092 " the given uri and writes to stdout\n"
00093 " -a - allow stale data\n"
00094 " -p n - use up to n pipeline slots\n"
00095 " -s - use new-style segmentation markers\n",
00096 progname);
00097 exit(1);
00098 }
00099
00100 static void
00101 mygettime(const struct ccn_gettime *self, struct ccn_timeval *result)
00102 {
00103 struct timeval now = {0};
00104 gettimeofday(&now, 0);
00105 result->s = now.tv_sec;
00106 result->micros = now.tv_usec;
00107 }
00108
00109 static struct ccn_gettime myticker = {
00110 "timer",
00111 &mygettime,
00112 1000000,
00113 NULL
00114 };
00115
00116 static void
00117 update_rtt(struct mydata *md, int incoming, unsigned slot)
00118 {
00119 struct timeval now = {0};
00120 unsigned t, delta, rtte;
00121
00122 if (!incoming && md->sendtime_slot == ~0)
00123 md->sendtime_slot = slot;
00124 if (slot != md->sendtime_slot)
00125 return;
00126 gettimeofday(&now, 0);
00127 t = ((unsigned)(now.tv_sec) * 1000000) + (unsigned)(now.tv_usec);
00128 if (incoming) {
00129 delta = t - md->sendtime;
00130 md->rtt = delta;
00131 if (delta <= 30000000) {
00132 rtte = md->rtte;
00133 if (delta > rtte)
00134 rtte = rtte + (rtte >> 3);
00135 else
00136 rtte = rtte - (rtte >> 7);
00137 if (rtte < 127)
00138 rtte = delta;
00139 md->rtte = rtte;
00140 }
00141 if (md->holefiller == NULL)
00142 md->holefiller = ccn_schedule_event(md->sched, 10000, &fill_holes, NULL, 0);
00143 md->sendtime_slot = ~0;
00144 if (logstream)
00145 fprintf(logstream,
00146 "%ld.%06u ccncatchunks2: "
00147 "%jd isent, %jd recvd, %jd junk, %jd holes, %jd t/o, %jd unvrf, "
00148 "%u curwin, %u rtt, %u rtte\n",
00149 (long)now.tv_sec,
00150 (unsigned)now.tv_usec,
00151 md->interests_sent,
00152 md->pkts_recvd,
00153 md->junk,
00154 md->holes,
00155 md->timeouts,
00156 md->unverified,
00157 md->curwindow,
00158 md->rtt,
00159 md->rtte
00160 );
00161 }
00162 else
00163 md->sendtime = t;
00164 }
00165
00166 static int
00167 reporter(struct ccn_schedule *sched, void *clienth,
00168 struct ccn_scheduled_event *ev, int flags)
00169 {
00170 struct timeval now = {0};
00171 struct mydata *md = clienth;
00172 gettimeofday(&now, 0);
00173 fflush(stdout);
00174 fprintf(stderr,
00175 "%ld.%06u ccncatchunks2[%d]: "
00176 "%jd isent, %jd recvd, %jd junk, %jd holes, %jd t/o, %jd unvrf, "
00177 "%u curwin, %u rtt, %u rtte\n",
00178 (long)now.tv_sec,
00179 (unsigned)now.tv_usec,
00180 (int)getpid(),
00181 md->interests_sent,
00182 md->pkts_recvd,
00183 md->junk,
00184 md->holes,
00185 md->timeouts,
00186 md->unverified,
00187 md->curwindow,
00188 md->rtt,
00189 md->rtte
00190 );
00191 if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00192 md->report = NULL;
00193 return(0);
00194 }
00195 return(3000000);
00196 }
00197
00198 void
00199 print_summary(struct mydata *md)
00200 {
00201 const char *expid;
00202 const char *dlm = " ";
00203 double elapsed = 0.0;
00204 intmax_t delivered_bytes;
00205 double rate = 0.0;
00206
00207 expid = getenv("CCN_EXPERIMENT_ID");
00208 if (expid == NULL)
00209 expid = dlm = "";
00210 gettimeofday(&md->stop_tv, 0);
00211 elapsed = (double)(long)(md->stop_tv.tv_sec - md->start_tv.tv_sec);
00212 elapsed += ((int)md->stop_tv.tv_usec - (int)md->start_tv.tv_usec)/1000000.0;
00213 delivered_bytes = md->delivered_bytes;
00214 if (elapsed > 0.00001)
00215 rate = delivered_bytes/elapsed;
00216 fprintf(stderr,
00217 "%ld.%06u ccncatchunks2[%d]: %s%s"
00218 "%jd bytes transferred in %.6f seconds (%.0f bytes/sec)"
00219 "\n",
00220 (long)md->stop_tv.tv_sec,
00221 (unsigned)md->stop_tv.tv_usec,
00222 (int)getpid(),
00223 expid,
00224 dlm,
00225 delivered_bytes,
00226 elapsed,
00227 rate
00228 );
00229 }
00230
00231 struct ccn_charbuf *
00232 make_template(struct mydata *md)
00233 {
00234 struct ccn_charbuf *templ = ccn_charbuf_create();
00235 ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00236 ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00237 ccn_charbuf_append_closer(templ);
00238
00239 ccn_charbuf_append_tt(templ, CCN_DTAG_MaxSuffixComponents, CCN_DTAG);
00240 ccnb_append_number(templ, 1);
00241 ccn_charbuf_append_closer(templ);
00242 if (md->allow_stale) {
00243 ccn_charbuf_append_tt(templ, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
00244 ccnb_append_number(templ, CCN_AOK_DEFAULT | CCN_AOK_STALE);
00245 ccn_charbuf_append_closer(templ);
00246 }
00247 ccn_charbuf_append_closer(templ);
00248 return(templ);
00249 }
00250
00251 static struct ccn_charbuf *
00252 sequenced_name(struct mydata *md, uintmax_t seq)
00253 {
00254 struct ccn_charbuf *name = NULL;
00255 struct ccn_charbuf *temp = NULL;
00256
00257 name = ccn_charbuf_create();
00258 ccn_charbuf_append(name, md->name->buf, md->name->length);
00259 if (md->use_decimal) {
00260 temp = ccn_charbuf_create();
00261 ccn_charbuf_putf(temp, "%ju", seq);
00262 ccn_name_append(name, temp->buf, temp->length);
00263 ccn_charbuf_destroy(&temp);
00264 }
00265 else
00266 ccn_name_append_numeric(name, CCN_MARKER_SEQNUM, seq);
00267 return(name);
00268 }
00269
00270 static void
00271 ask_more(struct mydata *md, uintmax_t seq)
00272 {
00273 struct ccn_charbuf *name = NULL;
00274 struct ccn_charbuf *templ = NULL;
00275 int res;
00276 unsigned slot;
00277 struct ccn_closure *cl = NULL;
00278
00279 slot = seq % PIPELIMIT;
00280 cl = &md->ooo[slot].closure;
00281 if (cl->intdata == -1)
00282 cl->intdata = seq;
00283 assert(cl->intdata == seq);
00284 assert(md->ooo[slot].raw_data_size == 0);
00285 name = sequenced_name(md, seq);
00286 templ = make_template(md);
00287 update_rtt(md, 0, slot);
00288 res = ccn_express_interest(md->h, name, cl, templ);
00289 if (res < 0) abort();
00290 md->interests_sent++;
00291 ccn_charbuf_destroy(&templ);
00292 ccn_charbuf_destroy(&name);
00293 if (seq == md->delivered + md->ooo_count)
00294 md->ooo_count++;
00295 assert(seq >= md->delivered);
00296 assert(seq < md->delivered + md->ooo_count);
00297 assert(md->ooo_count < PIPELIMIT);
00298 }
00299
00300 static enum ccn_upcall_res
00301 hole_filled(struct ccn_closure *selfp,
00302 enum ccn_upcall_kind kind,
00303 struct ccn_upcall_info *info)
00304 {
00305 if (kind == CCN_UPCALL_FINAL)
00306 free(selfp);
00307 return(CCN_UPCALL_RESULT_OK);
00308 }
00309
00310 static int
00311 fill_holes(struct ccn_schedule *sched, void *clienth,
00312 struct ccn_scheduled_event *ev, int flags)
00313 {
00314 struct mydata *md = clienth;
00315 struct ccn_charbuf *name = NULL;
00316 struct ccn_charbuf *templ = NULL;
00317 struct ccn_closure *cl = NULL;
00318 unsigned backoff;
00319 int delay;
00320
00321 if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00322 md->holefiller = NULL;
00323 return(0);
00324 }
00325 backoff = md->backoff;
00326 if (md->delivered == md->lastcheck && md->ooo_count > 0) {
00327 if (backoff == 0) {
00328 md->holes++;
00329 fprintf(stderr, "*** Hole at %jd\n", md->delivered);
00330 reporter(sched, md, NULL, 0);
00331 md->curwindow = 1;
00332 cl = calloc(1, sizeof(*cl));
00333 cl->p = &hole_filled;
00334 name = sequenced_name(md, md->delivered);
00335 templ = make_template(md);
00336 ccn_express_interest(md->h, name, cl, templ);
00337 md->interests_sent++;
00338 ccn_charbuf_destroy(&templ);
00339 ccn_charbuf_destroy(&name);
00340 }
00341 if ((6000000 >> backoff) > md->rtte)
00342 backoff++;
00343 }
00344 else {
00345 md->lastcheck = md->delivered;
00346 backoff = 0;
00347 }
00348 md->backoff = backoff;
00349 delay = (md->rtte << backoff);
00350 if (delay < 10000)
00351 delay = 10000;
00352 return(delay);
00353 }
00354
00355 static int
00356 is_final(struct ccn_upcall_info *info)
00357 {
00358
00359 const unsigned char *ccnb;
00360 size_t ccnb_size;
00361 ccnb = info->content_ccnb;
00362 if (ccnb == NULL || info->pco == NULL)
00363 return(0);
00364 ccnb_size = info->pco->offset[CCN_PCO_E];
00365 if (info->pco->offset[CCN_PCO_B_FinalBlockID] !=
00366 info->pco->offset[CCN_PCO_E_FinalBlockID]) {
00367 const unsigned char *finalid = NULL;
00368 size_t finalid_size = 0;
00369 const unsigned char *nameid = NULL;
00370 size_t nameid_size = 0;
00371 struct ccn_indexbuf *cc = info->content_comps;
00372 ccn_ref_tagged_BLOB(CCN_DTAG_FinalBlockID, ccnb,
00373 info->pco->offset[CCN_PCO_B_FinalBlockID],
00374 info->pco->offset[CCN_PCO_E_FinalBlockID],
00375 &finalid,
00376 &finalid_size);
00377 if (cc->n < 2) abort();
00378 ccn_ref_tagged_BLOB(CCN_DTAG_Component, ccnb,
00379 cc->buf[cc->n - 2],
00380 cc->buf[cc->n - 1],
00381 &nameid,
00382 &nameid_size);
00383 if (finalid_size == nameid_size &&
00384 0 == memcmp(finalid, nameid, nameid_size))
00385 return(1);
00386 }
00387 return(0);
00388 }
00389
00390 enum ccn_upcall_res
00391 incoming_content(struct ccn_closure *selfp,
00392 enum ccn_upcall_kind kind,
00393 struct ccn_upcall_info *info)
00394 {
00395 const unsigned char *ccnb = NULL;
00396 size_t ccnb_size = 0;
00397 const unsigned char *data = NULL;
00398 size_t data_size = 0;
00399 size_t written;
00400 int res;
00401 struct mydata *md = selfp->data;
00402 unsigned slot;
00403
00404 if (kind == CCN_UPCALL_FINAL) {
00405 selfp->intdata = -1;
00406 return(CCN_UPCALL_RESULT_OK);
00407 }
00408 GOT_HERE();
00409 if (kind == CCN_UPCALL_INTEREST_TIMED_OUT) {
00410 md->timeouts++;
00411 if (selfp->refcount > 1 || selfp->intdata == -1)
00412 return(CCN_UPCALL_RESULT_OK);
00413 md->interests_sent++;
00414 md->curwindow = 1;
00415
00416 return(CCN_UPCALL_RESULT_REEXPRESS);
00417 }
00418 GOT_HERE();
00419 if (kind != CCN_UPCALL_CONTENT && kind != CCN_UPCALL_CONTENT_UNVERIFIED)
00420 return(CCN_UPCALL_RESULT_ERR);
00421 assert(md != NULL);
00422 if (kind == CCN_UPCALL_CONTENT_UNVERIFIED) {
00423 if (md->pkts_recvd == 0)
00424 return(CCN_UPCALL_RESULT_VERIFY);
00425 md->unverified++;
00426 }
00427 md->pkts_recvd++;
00428 if (selfp->intdata == -1) {
00429
00430 md->dups++;
00431 return(CCN_UPCALL_RESULT_OK);
00432 }
00433 ccnb = info->content_ccnb;
00434 ccnb_size = info->pco->offset[CCN_PCO_E];
00435 res = ccn_content_get_value(ccnb, ccnb_size, info->pco, &data, &data_size);
00436 if (res < 0) abort();
00437 GOT_HERE();
00438
00439 md->co_bytes_recvd += data_size;
00440 slot = ((uintptr_t)selfp->intdata) % PIPELIMIT;
00441 assert(selfp == &md->ooo[slot].closure);
00442 if (is_final(info)) {
00443 GOT_HERE();
00444 md->finalslot = slot;
00445 }
00446 if (slot != md->ooo_base || md->ooo_count == 0) {
00447
00448 struct ooodata *ooo = &md->ooo[slot];
00449 if (ooo->raw_data_size == 0) {
00450 GOT_HERE();
00451 update_rtt(md, 1, slot);
00452 ooo->raw_data = malloc(data_size);
00453 memcpy(ooo->raw_data, data, data_size);
00454 ooo->raw_data_size = data_size + 1;
00455 }
00456 else
00457 md->dups++;
00458 if (md->curwindow > 1)
00459 md->curwindow--;
00460 }
00461 else {
00462 assert(md->ooo[slot].raw_data_size == 0);
00463 update_rtt(md, 1, slot);
00464 md->ooo[slot].closure.intdata = -1;
00465 md->delivered++;
00466 md->delivered_bytes += data_size;
00467 written = fwrite(data, data_size, 1, stdout);
00468 if (written != 1)
00469 exit(1);
00470
00471 if (slot == md->finalslot) {
00472 GOT_HERE();
00473 ccn_schedule_destroy(&md->sched);
00474 print_summary(md);
00475 exit(0);
00476 }
00477 md->ooo_count--;
00478 slot = (slot + 1) % PIPELIMIT;
00479 if (md->curwindow < md->maxwindow)
00480 md->curwindow++;
00481 while (md->ooo_count > 0 && md->ooo[slot].raw_data_size != 0) {
00482 struct ooodata *ooo = &md->ooo[slot];
00483 md->delivered++;
00484 md->delivered_bytes += (ooo->raw_data_size - 1);
00485 written = fwrite(ooo->raw_data, ooo->raw_data_size - 1, 1, stdout);
00486 if (written != 1)
00487 exit(1);
00488
00489 if (slot == md->finalslot) {
00490 GOT_HERE();
00491 ccn_schedule_destroy(&md->sched);
00492 exit(0);
00493 }
00494 free(ooo->raw_data);
00495 ooo->raw_data = NULL;
00496 ooo->raw_data_size = 0;
00497 slot = (slot + 1) % PIPELIMIT;
00498 md->ooo_count--;
00499 }
00500 md->ooo_base = slot;
00501 }
00502
00503
00504 if (md->ooo_count < md->curwindow)
00505 ask_more(md, md->delivered + md->ooo_count);
00506 if (md->ooo_count < md->curwindow)
00507 ask_more(md, md->delivered + md->ooo_count);
00508
00509 return(CCN_UPCALL_RESULT_OK);
00510 }
00511
00512 int
00513 main(int argc, char **argv)
00514 {
00515 struct ccn *ccn = NULL;
00516 struct ccn_charbuf *name = NULL;
00517 struct ccn_closure *incoming = NULL;
00518 const char *arg = NULL;
00519 int res;
00520 int micros;
00521 int opt;
00522 struct mydata *mydata;
00523 int allow_stale = 0;
00524 int use_decimal = 1;
00525 int i;
00526 unsigned maxwindow = PIPELIMIT-1;
00527
00528 if (maxwindow > 31)
00529 maxwindow = 31;
00530
00531 while ((opt = getopt(argc, argv, "hap:s")) != -1) {
00532 switch (opt) {
00533 case 'a':
00534 allow_stale = 1;
00535 break;
00536 case 'p':
00537 res = atoi(optarg);
00538 if (1 <= res && res < PIPELIMIT)
00539 maxwindow = res;
00540 else
00541 usage(argv[0]);
00542 break;
00543 case 's':
00544 use_decimal = 0;
00545 break;
00546 case 'h':
00547 default:
00548 usage(argv[0]);
00549 }
00550 }
00551 arg = argv[optind];
00552 if (arg == NULL)
00553 usage(argv[0]);
00554 name = ccn_charbuf_create();
00555 res = ccn_name_from_uri(name, arg);
00556 if (res < 0) {
00557 fprintf(stderr, "%s: bad ccn URI: %s\n", argv[0], arg);
00558 exit(1);
00559 }
00560 if (argv[optind + 1] != NULL)
00561 fprintf(stderr, "%s warning: extra arguments ignored\n", argv[0]);
00562 ccn = ccn_create();
00563 if (ccn_connect(ccn, NULL) == -1) {
00564 perror("Could not connect to ccnd");
00565 exit(1);
00566 }
00567
00568 mydata = calloc(1, sizeof(*mydata));
00569 mydata->h = ccn;
00570 mydata->name = name;
00571 mydata->allow_stale = allow_stale;
00572 mydata->use_decimal = use_decimal;
00573 mydata->excl = NULL;
00574 mydata->sched = ccn_schedule_create(mydata, &myticker);
00575 mydata->report = ccn_schedule_event(mydata->sched, 0, &reporter, NULL, 0);
00576 mydata->holefiller = NULL;
00577 mydata->maxwindow = maxwindow;
00578 mydata->finalslot = ~0;
00579 for (i = 0; i < PIPELIMIT; i++) {
00580 incoming = &mydata->ooo[i].closure;
00581 incoming->p = &incoming_content;
00582 incoming->data = mydata;
00583 incoming->intdata = -1;
00584 }
00585 mydata->ooo_base = 0;
00586 mydata->ooo_count = 0;
00587 mydata->curwindow = 1;
00588 gettimeofday(&mydata->start_tv, 0);
00589 logstream = NULL;
00590
00591 ask_more(mydata, 0);
00592
00593 res = ccn_run(ccn, 500);
00594 if (mydata->delivered == 0) {
00595 fprintf(stderr, "%s: not found: %s\n", argv[0], arg);
00596 exit(1);
00597 }
00598
00599 while (res >= 0) {
00600 micros = ccn_schedule_run(mydata->sched);
00601 if (micros < 0)
00602 micros = 10000000;
00603 res = ccn_run(ccn, micros / 1000);
00604 }
00605 ccn_schedule_destroy(&mydata->sched);
00606 print_summary(mydata);
00607 ccn_destroy(&ccn);
00608 exit(res < 0);
00609 }