1 /*
2 ** $Id: api.c 1038 2009-01-07 17:33:42Z aaron $
3 */
4 /************************************************************************
5 * *
6 * Copyright (C) 2002 *
7 * Internet2 *
8 * All Rights Reserved *
9 * *
10 ************************************************************************/
11 /*
12 ** File: api.c
13 **
14 ** Author: Jeff W. Boote
15 ** Anatoly Karp
16 **
17 ** Date: Fri Mar 29 15:36:44 2002
18 **
19 ** Description:
20 */
21 #include "./owampP.h"
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <signal.h>
29 #include <netinet/in.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <libgen.h>
33
34
35 /*
36 * Function: OWPGetContext
37 *
38 * Description:
39 * Returns the context pointer that was referenced when the
40 * given control connection was created.
41 *
42 * In Args:
43 *
44 * Out Args:
45 *
46 * Scope:
47 * Returns:
48 * Side Effect:
49 */
50 OWPContext
OWPGetContext(OWPControl cntrl)51 OWPGetContext(
52 OWPControl cntrl
53 )
54 {
55 return cntrl->ctx;
56 }
57
58 /*
59 * Function: OWPGetMode
60 *
61 * Description:
62 * Returns the "mode" of the control connection.
63 *
64 * In Args:
65 *
66 * Out Args:
67 *
68 * Scope:
69 * Returns:
70 * Side Effect:
71 */
72 OWPSessionMode
OWPGetMode(OWPControl cntrl)73 OWPGetMode(
74 OWPControl cntrl
75 )
76 {
77 return cntrl->mode;
78 }
79
80 /*
81 * Function: OWPControlFD
82 *
83 * Description:
84 *
85 * In Args:
86 *
87 * Out Args:
88 *
89 * Scope:
90 * Returns:
91 * Side Effect:
92 */
93 int
OWPControlFD(OWPControl cntrl)94 OWPControlFD(
95 OWPControl cntrl
96 )
97 {
98 return cntrl->sockfd;
99 }
100
101 /*
102 * Function: OWPGetRTTBound
103 *
104 * Description: Returns a very rough estimate of the upper-bound rtt to
105 * the server.
106 *
107 * In Args:
108 *
109 * Out Args:
110 *
111 * Scope:
112 * Returns:
113 * bound or 0 if unavailable
114 * Side Effect:
115 */
116 OWPNum64
OWPGetRTTBound(OWPControl cntrl)117 OWPGetRTTBound(
118 OWPControl cntrl
119 )
120 {
121 return cntrl->rtt_bound;
122 }
123
124 /*
125 * Function: _OWPFailControlSession
126 *
127 * Description:
128 * Simple convienience to set the state and return the failure at
129 * the same time.
130 *
131 * In Args:
132 *
133 * Out Args:
134 *
135 * Scope:
136 * Returns:
137 * Side Effect:
138 */
139 OWPErrSeverity
_OWPFailControlSession(OWPControl cntrl,int level)140 _OWPFailControlSession(
141 OWPControl cntrl,
142 int level
143 )
144 {
145 cntrl->state = _OWPStateInvalid;
146 return (OWPErrSeverity)level;
147 }
148
149 /*
150 * Function: _OWPTestSessionAlloc
151 *
152 * Description:
153 * This function is used to allocate/initialize the memory record used
154 * to maintain state information about a "configured" test.
155 *
156 * In Args:
157 *
158 * Out Args:
159 *
160 * Scope:
161 * Returns:
162 * Side Effect:
163 */
164 OWPTestSession
_OWPTestSessionAlloc(OWPControl cntrl,I2Addr sender,OWPBoolean conf_sender,I2Addr receiver,OWPBoolean conf_receiver,OWPTestSpec * test_spec)165 _OWPTestSessionAlloc(
166 OWPControl cntrl,
167 I2Addr sender,
168 OWPBoolean conf_sender,
169 I2Addr receiver,
170 OWPBoolean conf_receiver,
171 OWPTestSpec *test_spec
172 )
173 {
174 OWPTestSession test;
175
176 /*
177 * Address records must exist.
178 */
179 if(!sender || ! receiver){
180 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
181 "_OWPTestSessionAlloc:Invalid Addr arg");
182 return NULL;
183 }
184
185 if(!(test = calloc(1,sizeof(OWPTestSessionRec)))){
186 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
187 "calloc(1,OWPTestSessionRec): %M");
188 return NULL;
189 }
190
191 /*
192 * Initialize address records and test description record fields.
193 */
194 test->cntrl = cntrl;
195 test->sender = sender;
196 test->conf_sender = conf_sender;
197 test->receiver = receiver;
198 test->conf_receiver = conf_receiver;
199 memcpy(&test->test_spec,test_spec,sizeof(OWPTestSpec));
200
201 /*
202 * Allocate memory for slot records if they won't fit in the
203 * pre-allocated "buffer" already associated with the TestSession
204 * record. Then copy the slot records.
205 * (From the server side, slots will be 0 at this point - the
206 * SessionRecord is allocated before reading the slots off the
207 * socket so the SessionRecord slot "buffer" can potentially be used.)
208 */
209 if(test->test_spec.slots){
210 if(test->test_spec.nslots > _OWPSLOT_BUFSIZE){
211 if(!(test->test_spec.slots =
212 calloc(test->test_spec.nslots,
213 sizeof(OWPSlot)))){
214 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
215 "calloc(%d,OWPSlot): %M",
216 test->test_spec.nslots);
217 free(test);
218 return NULL;
219 }
220 }else{
221 test->test_spec.slots = test->slot_buffer;
222 }
223 memcpy(test->test_spec.slots,test_spec->slots,
224 test_spec->nslots*sizeof(OWPSlot));
225 }
226
227 return test;
228 }
229
230 /*
231 * Function: _OWPTestSessionFree
232 *
233 * Description:
234 * This function is used to free the memory associated with a "configured"
235 * test session.
236 *
237 * In Args:
238 *
239 * Out Args:
240 *
241 * Scope:
242 * Returns:
243 * Side Effect:
244 */
245 OWPErrSeverity
_OWPTestSessionFree(OWPTestSession tsession,OWPAcceptType aval)246 _OWPTestSessionFree(
247 OWPTestSession tsession,
248 OWPAcceptType aval
249 )
250 {
251 OWPTestSession *sptr;
252 OWPAcceptType alocal = aval;
253 OWPErrSeverity err=OWPErrOK;
254
255 if(!tsession){
256 return OWPErrOK;
257 }
258
259 /*
260 * remove this tsession from the cntrl->tests lists.
261 */
262 for(sptr = &tsession->cntrl->tests;*sptr;sptr = &(*sptr)->next){
263 if(*sptr == tsession){
264 *sptr = tsession->next;
265 break;
266 }
267 }
268
269 if(tsession->endpoint){
270 _OWPEndpointFree(tsession->endpoint,&alocal,&err);
271 }
272
273 if(tsession->closure){
274 _OWPCallTestComplete(tsession,aval);
275 }
276
277 I2AddrFree(tsession->sender);
278 I2AddrFree(tsession->receiver);
279
280 if(tsession->sctx){
281 OWPScheduleContextFree(tsession->sctx);
282 }
283
284 if(tsession->test_spec.slots &&
285 (tsession->test_spec.slots != tsession->slot_buffer)){
286 free(tsession->test_spec.slots);
287 }
288
289 free(tsession);
290
291 return err;
292 }
293
294
295 /*
296 * Function: _OWPCreateSID
297 *
298 * Description:
299 * Generate a "unique" SID from addr(4)/time(8)/random(4) values.
300 *
301 * In Args:
302 *
303 * Out Args:
304 *
305 * Scope:
306 * Returns:
307 * 0 on success
308 * Side Effect:
309 */
310 int
_OWPCreateSID(OWPTestSession tsession)311 _OWPCreateSID(
312 OWPTestSession tsession
313 )
314 {
315 OWPTimeStamp tstamp;
316 uint8_t *aptr;
317 struct sockaddr *saddr;
318
319 if( !(saddr = I2AddrSAddr(tsession->receiver,NULL))){
320 OWPError(tsession->cntrl->ctx,OWPErrFATAL,OWPErrUNSUPPORTED,
321 "_OWPCreateSID: Invalid socket address");
322 return 1;
323 }
324
325 #ifdef AF_INET6
326 if(saddr->sa_family == AF_INET6){
327 struct sockaddr_in6 *s6;
328
329 s6 = (struct sockaddr_in6*)saddr;
330 /* point at last 4 bytes of addr */
331 aptr = &s6->sin6_addr.s6_addr[12];
332 }else
333 #endif
334 if(saddr->sa_family == AF_INET){
335 struct sockaddr_in *s4;
336
337 s4 = (struct sockaddr_in*)saddr;
338 aptr = (uint8_t*)&s4->sin_addr;
339 }
340 else{
341 OWPError(tsession->cntrl->ctx,OWPErrFATAL,OWPErrUNSUPPORTED,
342 "_OWPCreateSID:Unknown address family");
343 return 1;
344 }
345
346 memcpy(&tsession->sid[0],aptr,4);
347
348 (void)OWPGetTimeOfDay(tsession->cntrl->ctx,&tstamp);
349 _OWPEncodeTimeStamp(&tsession->sid[4],&tstamp);
350
351 if(I2RandomBytes(tsession->cntrl->ctx->rand_src,
352 (uint8_t *)&tsession->sid[12],4)
353 != 0){
354 return 1;
355 }
356
357 return 0;
358 }
359
360 OWPPacketSizeT
OWPTestPayloadSize(OWPSessionMode mode,uint32_t padding)361 OWPTestPayloadSize(
362 OWPSessionMode mode,
363 uint32_t padding
364 )
365 {
366 OWPPacketSizeT msg_size;
367
368 switch (mode) {
369 case OWP_MODE_OPEN:
370 msg_size = 14;
371 break;
372 case OWP_MODE_AUTHENTICATED:
373 case OWP_MODE_ENCRYPTED:
374 msg_size = 48;
375 break;
376 default:
377 return 0;
378 /* UNREACHED */
379 }
380
381 return msg_size + padding;
382 }
383
384 /*
385 * Function: OWPTestPacketRate
386 *
387 * Description:
388 * This function returns the # packets/ second as a double.
389 *
390 * In Args:
391 *
392 * Out Args:
393 *
394 * Scope:
395 * Returns:
396 * Side Effect:
397 */
398 double
OWPTestPacketRate(OWPContext ctx,OWPTestSpec * tspec)399 OWPTestPacketRate(
400 OWPContext ctx,
401 OWPTestSpec *tspec
402 )
403 {
404 OWPNum64 duration = OWPULongToNum64(0);
405 uint32_t i;
406
407 if(!tspec){
408 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
409 "OWPTestPacketRate: Invalid tspec arg");
410 return 0;
411 }
412
413 if(!tspec->nslots || !tspec->slots){
414 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
415 "OWPTestPacketRate: Invalid empty test specification");
416 return 0;
417 }
418
419 for(i=0;i<tspec->nslots;i++){
420 duration = OWPNum64Add(duration,tspec->slots[i].any.mean_delay);
421 }
422
423 if(duration <= 0){
424 return 0;
425 }
426
427 return (double)tspec->nslots / OWPNum64ToDouble(duration);
428 }
429
430 /* These lengths assume no IP options. */
431 #define OWP_IP4_HDR_SIZE 20 /* rfc 791 */
432 #define OWP_IP6_HDR_SIZE 40 /* rfc 2460 */
433 #define OWP_UDP_HDR_SIZE 8 /* rfc 768 */
434
435 /*
436 ** Given the protocol family, OWAMP mode and packet padding,
437 ** compute the size of resulting full IP packet.
438 */
439 OWPPacketSizeT
OWPTestPacketSize(int af,OWPSessionMode mode,uint32_t padding)440 OWPTestPacketSize(
441 int af, /* AF_INET, AF_INET6 */
442 OWPSessionMode mode,
443 uint32_t padding
444 )
445 {
446 OWPPacketSizeT payload_size, header_size;
447
448 switch (af) {
449 case AF_INET:
450 header_size = OWP_IP4_HDR_SIZE + OWP_UDP_HDR_SIZE;
451 break;
452 case AF_INET6:
453 header_size = OWP_IP6_HDR_SIZE + OWP_UDP_HDR_SIZE;
454 break;
455 default:
456 return 0;
457 /* UNREACHED */
458 }
459
460 if(!(payload_size = OWPTestPayloadSize(mode,padding)))
461 return 0;
462
463 return payload_size + header_size;
464 }
465
466 /*
467 * Function: OWPTestPacketBandwidth
468 *
469 * Description:
470 * returns the average bandwidth requirements of the given test using
471 * the given address family, and authentication mode.
472 *
473 * In Args:
474 *
475 * Out Args:
476 *
477 * Scope:
478 * Returns:
479 * Side Effect:
480 */
481 double
OWPTestPacketBandwidth(OWPContext ctx,int af,OWPSessionMode mode,OWPTestSpec * tspec)482 OWPTestPacketBandwidth(
483 OWPContext ctx,
484 int af,
485 OWPSessionMode mode,
486 OWPTestSpec *tspec
487 )
488 {
489 if(!tspec){
490 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
491 "OWPTestPacketBandwidth: Invalid tspec arg");
492 return 0;
493 }
494
495 return OWPTestPacketRate(ctx,tspec) *
496 OWPTestPacketSize(af,mode,tspec->packet_size_padding) * 8;
497 }
498
499 /*
500 * Function: OWPSessionStatus
501 *
502 * Description:
503 * This function returns the "status" of the test session identified
504 * by the sid. "send" indicates which "side" of the test to retrieve
505 * information about.
506 *
507 * In Args:
508 *
509 * Out Args:
510 *
511 * Scope:
512 * Returns: True if status was available, False otherwise.
513 * aval contains the actual "status":
514 * <0 Test is not yet complete
515 * >=0 Valid OWPAcceptType - see enum for meaning.
516 * Side Effect:
517 */
518 OWPBoolean
OWPSessionStatus(OWPControl cntrl,OWPSID sid,OWPAcceptType * aval)519 OWPSessionStatus(
520 OWPControl cntrl,
521 OWPSID sid,
522 OWPAcceptType *aval
523 )
524 {
525 OWPTestSession tsession;
526 OWPErrSeverity err;
527
528 /*
529 * First find the tsession record for this test.
530 */
531 for(tsession=cntrl->tests;tsession;tsession=tsession->next)
532 if(memcmp(sid,tsession->sid,sizeof(OWPSID)) == 0)
533 goto found;
534
535 return False;
536
537 found:
538 if(tsession->endpoint){
539 _OWPEndpointStatus(tsession->endpoint,aval,&err);
540 return True;
541 }
542
543 return False;
544 }
545
546 int
OWPSessionsActive(OWPControl cntrl,OWPAcceptType * aval)547 OWPSessionsActive(
548 OWPControl cntrl,
549 OWPAcceptType *aval
550 )
551 {
552 OWPTestSession tsession;
553 OWPAcceptType laval;
554 OWPAcceptType raval = 0;
555 int n=0;
556 OWPErrSeverity err;
557
558 for(tsession = cntrl->tests;tsession;tsession = tsession->next){
559 if(tsession->endpoint){
560 /* initialize laval before querying status */
561 laval = OWP_CNTRL_ACCEPT;
562 _OWPEndpointStatus(tsession->endpoint,&laval,&err);
563 if(laval < 0){
564 n++;
565 } else{
566 raval = MAX(laval,raval);
567 }
568 }
569 }
570
571 if(aval)
572 *aval = raval;
573
574 return n;
575 }
576
577 /*
578 * Function: _OWPStopSendSessions
579 *
580 * Description:
581 * This function is used to stop send sessions. Skip
582 * records are somewhat validated for use in the
583 * _OWPWriteStopSessions function.
584 *
585 * In Args:
586 *
587 * Out Args:
588 *
589 * Scope:
590 * Returns:
591 * Side Effect:
592 */
593 static OWPErrSeverity
_OWPStopSendSessions(OWPControl cntrl,OWPAcceptType * acceptval_ret,uint32_t * num_sessions)594 _OWPStopSendSessions(
595 OWPControl cntrl,
596 OWPAcceptType *acceptval_ret, /* in/out */
597 uint32_t *num_sessions
598 )
599 {
600 OWPErrSeverity err,err2=OWPErrOK;
601 OWPAcceptType aval=OWP_CNTRL_ACCEPT;
602 OWPAcceptType *acceptval = &aval;
603 OWPTestSession sptr;
604 uint32_t num_senders=0;
605
606 if(acceptval_ret){
607 acceptval = acceptval_ret;
608 }
609 *num_sessions = 0;
610
611 /*
612 * Stop each session - count the "send" sessions and verify that
613 * the "skip records" saved at each fd are at least a consistent size.
614 */
615 for(sptr=cntrl->tests; sptr; sptr = sptr->next){
616 uint32_t sdr[2];
617 uint32_t nskip;
618 struct stat sbuf;
619
620 /*
621 * Validity check.
622 */
623 if(!sptr->endpoint){
624 OWPError(cntrl->ctx,OWPErrWARNING,EINVAL,
625 "_OWPStopSendSessions: no endpoint state!");
626 *acceptval = OWP_CNTRL_FAILURE;
627 break;
628 }
629
630 /*
631 * Receive sessions are not done here.
632 */
633 if(!sptr->endpoint->send) continue;
634
635 /*
636 * Stop local sessions
637 */
638 _OWPEndpointStop(sptr->endpoint,acceptval,&err);
639 err2 = MIN(err,err2);
640
641 /* count senders for inclusion in StopSessions message */
642 num_senders++;
643
644 /*
645 * simple check to validate skip records:
646 * Just verify size of file matches reported number
647 * of skip records.
648 */
649 if(fstat(sptr->endpoint->skiprecfd,&sbuf) != 0){
650 OWPError(cntrl->ctx,OWPErrWARNING,errno,"fstat(skiprecfd): %M");
651 *acceptval = OWP_CNTRL_FAILURE;
652 err2 = MIN(OWPErrWARNING,err2);
653 continue;
654 }
655
656 /*
657 * Seek to beginning of file for reading.
658 */
659 if(lseek(sptr->endpoint->skiprecfd,0,SEEK_SET) == -1){
660 OWPError(cntrl->ctx,OWPErrWARNING,errno,"lseek(skiprecfd,0): %M");
661 *acceptval = OWP_CNTRL_FAILURE;
662 err2 = MIN(OWPErrWARNING,err2);
663 continue;
664 }
665
666 /*
667 * Read next_seqno and num_skips for verification purposes.
668 * (IGNORE intr for this local file i/o)
669 */
670 if(I2Readn(sptr->endpoint->skiprecfd,sdr,8) != 8){
671 OWPError(cntrl->ctx,OWPErrWARNING,errno,"I2Readn(skiprecfd): %M");
672 *acceptval = OWP_CNTRL_FAILURE;
673 err2 = MIN(OWPErrWARNING,err2);
674 continue;
675 }
676
677 /*
678 * Reset fd to beginning of file for reading.
679 */
680 if(lseek(sptr->endpoint->skiprecfd,0,SEEK_SET) == -1){
681 OWPError(cntrl->ctx,OWPErrWARNING,errno,"lseek(skiprecfd,0): %M");
682 *acceptval = OWP_CNTRL_FAILURE;
683 err2 = MIN(OWPErrWARNING,err2);
684 continue;
685 }
686
687 nskip = ntohl(sdr[1]);
688
689 /*
690 * Each skip record is 8 bytes, plus 8 bytes for next_seqno and
691 * num_skip_records means: filesize == ((nskip+1)*8)
692 */
693 if((off_t)((nskip+1)*8) != sbuf.st_size){
694 OWPError(cntrl->ctx,OWPErrWARNING,EINVAL,
695 "_OWPStopSendSessions: Invalid skiprecfd data");
696 *acceptval = OWP_CNTRL_FAILURE;
697 err2 = MIN(OWPErrWARNING,err2);
698 continue;
699 }
700
701 sptr->endpoint->skiprecsize = sbuf.st_size;
702 }
703
704 *num_sessions = num_senders;
705 return err2;
706 }
707
708 /*
709 * Function: _OWPStopRecvSessions
710 *
711 * Description:
712 * This function is used to stop recv sessions.
713 * Skip records and next_seqno reported from the send side
714 * of the test will be used to finish writing the datafile.
715 *
716 * In Args:
717 *
718 * Out Args:
719 *
720 * Scope:
721 * Returns:
722 * Side Effect:
723 */
724 static OWPErrSeverity
_OWPStopRecvSessions(OWPControl cntrl,OWPAcceptType * acceptval_ret)725 _OWPStopRecvSessions(
726 OWPControl cntrl,
727 OWPAcceptType *acceptval_ret
728 )
729 {
730 OWPErrSeverity err,err2=OWPErrOK;
731 OWPAcceptType aval=OWP_CNTRL_ACCEPT;
732 OWPAcceptType *acceptval = &aval;
733 OWPTestSession sptr;
734
735 if(acceptval_ret){
736 acceptval = acceptval_ret;
737 }
738
739 /*
740 * Stop each "recv" session
741 */
742 for(sptr=cntrl->tests; sptr; sptr = sptr->next){
743 /*
744 * Validity check.
745 */
746 if(!sptr->endpoint){
747 OWPError(cntrl->ctx,OWPErrWARNING,EINVAL,
748 "_OWPStopRecvSessions: no endpoint state!");
749 *acceptval = OWP_CNTRL_FAILURE;
750 break;
751 }
752
753 /*
754 * Send sessions not done here.
755 */
756 if(sptr->endpoint->send) continue;
757
758 /*
759 * Stop local sessions
760 */
761 _OWPEndpointStop(sptr->endpoint,acceptval,&err);
762 err2 = MIN(err,err2);
763 }
764
765 return err2;
766 }
767
768 /*
769 * Function: _OWPCleanDataRecs
770 *
771 * Description:
772 * Function is used to remove data records from the recieve
773 * side test based on the information from the StopSession
774 * message. (stoptime, next_seqno)
775 *
776 * If StopSession message is not exchanged, then next_seqno
777 * MUST be set to 0xFFFFFFFF.
778 *
779 * The caller should have locked the file before calling this.
780 * In Args:
781 *
782 * Out Args:
783 *
784 * Scope:
785 * Returns:
786 * Side Effect:
787 */
788 OWPBoolean
_OWPCleanDataRecs(OWPContext ctx,OWPTestSession tptr,uint32_t next_seqno,OWPTimeStamp stoptime,uint32_t * max_recv_rtn,off_t * off_start_rtn)789 _OWPCleanDataRecs(
790 OWPContext ctx,
791 OWPTestSession tptr,
792 uint32_t next_seqno,
793 OWPTimeStamp stoptime,
794 uint32_t *max_recv_rtn,
795 off_t *off_start_rtn
796 )
797 {
798 FILE *rfp, *wfp;
799 char sid_name[sizeof(OWPSID)*2+1];
800 off_t toff;
801 OWPDataRec rec;
802 _OWPSessionHeaderInitialRec fhdr;
803 char rbuf[_OWP_MAXDATAREC_SIZE];
804 uint32_t j;
805 uint32_t lowI,midI,highI,num_recs;
806 OWPNum64 lowR,midR,highR,threshR;
807 uint32_t max_recv_data;
808 uint32_t *max_recv = &max_recv_data;
809 off_t off_start_data;
810 off_t *off_start = &off_start_data;
811
812 if(max_recv_rtn)
813 max_recv = max_recv_rtn;
814 *max_recv = 0;
815 if(off_start_rtn)
816 off_start = off_start_rtn;
817 *off_start = 0;
818
819
820 I2HexEncode(sid_name,tptr->sid,sizeof(OWPSID));
821
822 rfp = tptr->endpoint->datafile;
823 wfp = rfp;
824
825 /*
826 * Read needed file header info.
827 */
828 if(!_OWPReadDataHeaderInitial(ctx,rfp,&fhdr)){
829 goto err;
830 }
831
832 /*
833 * Compute number of data records currently in file using filesizes.
834 * (Verify that disk space is a multiple of datarec size too...)
835 */
836 toff = fhdr.sbuf.st_size - fhdr.oset_datarecs;
837 if(toff % fhdr.rec_size){
838 OWPError(ctx,OWPErrFATAL,EFTYPE,
839 "_OWPCleanDataRecs: Invalid records for sid(%s)",
840 sid_name);
841 goto err;
842 }
843 fhdr.num_datarecs = num_recs = toff / fhdr.rec_size;
844
845 /*
846 * If there is no data, this is a very simple file...
847 */
848 if(!fhdr.num_datarecs) goto clean_data;
849
850 /*
851 * Seek to beginning of data records.
852 */
853 if(fseeko(rfp,fhdr.oset_datarecs,SEEK_SET) != 0){
854 OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
855 goto err;
856 }
857
858 /*
859 * Delete data records with a computed send time later than
860 * currenttime (ie. stoptime) - timeout as per section 3.8 RFC 4656.
861 *
862 * To do this is somewhat non-trivial. The records in the file
863 * are sorted by recv time. There is a relationship between
864 * recv time and send time because of the "timeout" parameter
865 * of a test. A packet is only accepted and saved if the recv
866 * time is within "timeout" of the recv time. Therefore, this
867 * algorithm starts by finding the first packet record in the
868 * file with a recv time greater than (stoptime - (2 * timeout)).
869 * (recv time can be timeout less than send time if clocks are
870 * offset).
871 *
872 * Additionally, the next_seqno from the StopSessions message
873 * comes into play. Let the presumed sendtime of next_seqno be
874 * next_seqno_time. Then, the parsing may need to start
875 * as early as (next_seqno_time - (2 * timeout)).
876 *
877 * Therefore, the search algorithm actually looks for the minimum
878 * of those two values.
879 *
880 * lowI will be the index to the packet record with the
881 * largest recv time less than the threshold (stoptime - (2 * timeout))
882 * upon completion of the binary search. (If one exists.)
883 *
884 * After the binary search, the algorithm sequentially goes
885 * forward through the file deleting all packet records with
886 * (sendtime > (stoptime - timeout)). During this pass, if any
887 * index is >= next_seqno, the entire session will be declared
888 * invalid. Additionally, any LostPacket records with
889 * index >= next_seqno will be removed.
890 *
891 */
892 if(next_seqno == 0xFFFFFFFF){
893 next_seqno = tptr->test_spec.npackets;
894 }
895
896 if(next_seqno > tptr->test_spec.npackets){
897 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
898 "_OWPCleanDataRecs: Invalid \'next_seqno\': %lu",next_seqno);
899 goto err;
900 }
901
902 /*
903 * First use an interpolated binary search to find the "threshold"
904 * point in the file.
905 */
906
907 /* Initializing variables for search. */
908
909 /* find threshold time. MIN(stoptime,next_seqno_time) - (2 * timeout) */
910 if(OWPScheduleContextReset(tptr->sctx,NULL,NULL) != OWPErrOK){
911 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
912 "_OWPCleanDataRecs: SchedulecontextReset(): FAILED");
913 goto err;
914 }
915
916 /* find next_seqno_time */
917 lowI = 0;
918 threshR = tptr->test_spec.start_time;
919 while(lowI < next_seqno){
920 threshR = OWPNum64Add(threshR,
921 OWPScheduleContextGenerateNextDelta(tptr->sctx));
922 lowI++;
923 }
924
925 /* find MIN(next_seqno_time,stoptime) */
926 threshR = OWPNum64Min(threshR,stoptime.owptime);
927
928 /*
929 * Set actual threshold to 2*timeout less than that to deal with
930 * offset clocks.
931 */
932 threshR = OWPNum64Sub(threshR,
933 OWPNum64Mult(tptr->test_spec.loss_timeout,OWPULongToNum64(2)));
934 highI = fhdr.num_datarecs;
935 highR = stoptime.owptime;
936 lowI = 0;
937
938 /*
939 * Read the first packet record to get the recv(0) for lowR.
940 */
941 if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
942 OWPError(ctx,OWPErrFATAL,errno,
943 "fread(): Reading session file for sid(%s): %M",sid_name);
944 goto err;
945 }
946 if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
947 errno = EFTYPE;
948 OWPError(ctx,OWPErrFATAL,errno,
949 "_OWPCleanDataRecs: Invalid data record for sid(%s)",
950 sid_name);
951 goto err;
952 }
953
954 if(OWPIsLostRecord(&rec)){
955 lowR = OWPNum64Add(rec.send.owptime,tptr->test_spec.loss_timeout);
956 }
957 else{
958 lowR = rec.recv.owptime;
959 }
960
961 /*
962 * If lowR is not less than threshR than we are done.
963 */
964 if(!(OWPNum64Cmp(lowR,threshR) < 0)){
965 goto thresh_pos;
966 }
967
968 /*
969 * This loop is the meat of the interpolated binary search
970 */
971 while((highI - lowI) > 1){
972 OWPNum64 portion;
973 OWPNum64 range;
974
975 range = OWPNum64Sub(highR,lowR);
976
977 /*
978 * If there are multiple records with the same recv time,
979 * interpolation will fail - in this case fall back to strict
980 * binary.
981 */
982 if(!range){
983 midI = (highI - lowI) / 2;
984 }
985 else{
986 /*
987 * Interpolate
988 */
989 portion = OWPNum64Sub(threshR,lowR);
990 midI = lowI + ((OWPNum64ToDouble(portion) * (highI - lowI)) /
991 OWPNum64ToDouble(range));
992 if(midI == lowI) midI++;
993 }
994
995 /*
996 * determine offset from midI
997 */
998 toff = fhdr.oset_datarecs + midI * fhdr.rec_size;
999
1000 /*
1001 * Seek to midI data record.
1002 */
1003 if(fseeko(rfp,toff,SEEK_SET) != 0){
1004 OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
1005 goto err;
1006 }
1007
1008 /*
1009 * Read the packet record from midI.
1010 */
1011 if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
1012 OWPError(ctx,OWPErrFATAL,errno,
1013 "fread(): Reading session file for sid(%s): %M",
1014 sid_name);
1015 goto err;
1016 }
1017 if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
1018 errno = EFTYPE;
1019 OWPError(ctx,OWPErrFATAL,errno,
1020 "_OWPCleanDataRecs: Invalid data record for sid(%s)",
1021 sid_name);
1022 goto err;
1023 }
1024
1025 /*
1026 * If midR is less than thresh, update lowI. Otherwise,
1027 * update highI.
1028 */
1029 if(OWPIsLostRecord(&rec)){
1030 midR = OWPNum64Add(rec.send.owptime,
1031 tptr->test_spec.loss_timeout);
1032 }
1033 else{
1034 midR = rec.recv.owptime;
1035 }
1036 if(OWPNum64Cmp(midR,threshR) < 0){
1037 lowI = midI;
1038 lowR = midR;
1039 }
1040 else{
1041 highI = midI;
1042 highR = midR;
1043 }
1044 }
1045 thresh_pos:
1046
1047 /*
1048 * Now, step through all records lowI and after to examine the
1049 * sent time. The sent time must be less than (stop - timeout)
1050 * and the index must be less than next_seqno for the record
1051 * to be kept. (If index is greater than or equal to next_seqno,
1052 * and it is not a lost packet record, the entire session
1053 * MUST be deleted as per the spec.)
1054 *
1055 */
1056 *off_start = toff = fhdr.oset_datarecs + (lowI * fhdr.rec_size);
1057 threshR = OWPNum64Sub(stoptime.owptime,tptr->test_spec.loss_timeout);
1058
1059 /*
1060 * Seek to lowI data record to start parsing.
1061 */
1062 if(fseeko(rfp,toff,SEEK_SET) != 0){
1063 OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
1064 goto err;
1065 }
1066
1067 for(j=lowI;j<fhdr.num_datarecs;j++){
1068
1069 /*
1070 * Read the packet record from midI.
1071 */
1072 if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
1073 OWPError(ctx,OWPErrFATAL,errno,
1074 "fread(): Reading session file sid(%s): %M",
1075 sid_name);
1076 goto loop_err;
1077 }
1078 if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
1079 errno = EFTYPE;
1080 OWPError(ctx,OWPErrFATAL,errno,
1081 "_OWPCleanDataRecs: Invalid data record sid(%s)",
1082 sid_name);
1083 goto loop_err;
1084 }
1085
1086 /*
1087 * If the seq_no is >= next_seqno, and it is not a lost
1088 * packet record, then this session MUST be thrown out.
1089 * Otherwise, if the packet was not sent after threshR, then keep it
1090 * by writing it back into the file if necessary.
1091 * Finally, drop the packet.
1092 */
1093
1094 /* Invalid session */
1095 if((rec.seq_no >= next_seqno) && !OWPIsLostRecord(&rec)){
1096 errno = EFTYPE;
1097 OWPError(ctx,OWPErrFATAL,errno,
1098 "_OWPCleanDataRecs: Invalid data record (seq_no too large) sid(%s)",
1099 sid_name);
1100 goto loop_err;
1101 }
1102 /* Good record */
1103 else if((rec.seq_no < next_seqno) &&
1104 (OWPNum64Cmp(rec.send.owptime,threshR) <= 0)){
1105 *max_recv = MAX(rec.seq_no,*max_recv);
1106 if(wfp != rfp){
1107 if(fwrite(rbuf,fhdr.rec_size,1,wfp) != 1){
1108 OWPError(ctx,OWPErrFATAL,errno,
1109 "fwrite(): Writing session file sid(%s): %M",
1110 sid_name);
1111 goto loop_err;
1112 }
1113 }
1114 }
1115 /*
1116 * The packet record should not be kept.
1117 */
1118 else{
1119 num_recs--;
1120 /*
1121 * If wfp==rfp, then create another fp for wfp and point
1122 * it at the current record so it will be written over.
1123 */
1124 if(wfp == rfp){
1125 int newfd;
1126 char tmpfname[PATH_MAX];
1127 char *dname;
1128 char *tmpl = "owamp.XXXXXXXX";
1129
1130 /*
1131 * Need another file for bookkeeping... First determine
1132 * what dir to put it in.
1133 */
1134 dname = NULL;
1135 memset(tmpfname,'\0',sizeof(tmpfname));
1136
1137 /* put it in the same dir as session data if possible */
1138 if( strlen(tptr->endpoint->fname) > 0){
1139 strncpy(tmpfname,tptr->endpoint->fname,PATH_MAX);
1140 dname = dirname(tmpfname);
1141 }
1142
1143 /* otherwise use tmpdir */
1144 if( !dname){
1145 dname = getenv("TMPDIR");
1146 }
1147 if( !dname){
1148 dname = "/tmp";
1149 }
1150
1151 /* Make sure pathname will not over-run memory. */
1152 if(strlen(tmpl) + OWP_PATH_SEPARATOR_LEN + strlen(dname) >
1153 PATH_MAX){
1154 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1155 "_OWPCleanDataRecs: Unable to create temp file: Path Too Long");
1156 goto err;
1157 }
1158
1159 /* create template (fname) string for mkstemp */
1160 strcpy(tmpfname,dname);
1161 strcat(tmpfname,OWP_PATH_SEPARATOR);
1162 strcat(tmpfname,tmpl);
1163 if( (newfd = mkstemp(tmpfname)) < 0){
1164 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1165 "_OWPCleanDataRecs: mkstemp(%s): %M",
1166 tmpfname);
1167 goto err;
1168 }
1169
1170 /* immediately unlink - no need for a directory entry */
1171 if(unlink(tmpfname) != 0){
1172 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1173 "unlink(%s): %M",tmpfname);
1174 close(newfd);
1175 goto err;
1176 }
1177
1178 /*
1179 * Copy original file into tmpfile from the beginning
1180 * until just before the current record.
1181 */
1182 toff = fhdr.oset_datarecs + (j * fhdr.rec_size);
1183 if(I2CopyFile(OWPContextErrHandle(ctx),
1184 newfd,fileno(rfp),toff) != 0){
1185 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1186 "_OWPCleanDataRecs: Unable to copy session data: I2CopyFile(): %M");
1187 close(newfd);
1188 goto err;
1189 }
1190
1191 if( !(wfp = fdopen(newfd,"r+b"))){
1192 OWPError(ctx,OWPErrFATAL,errno,"fdopen(%d): %M",
1193 newfd);
1194 close(newfd);
1195 goto err;
1196 }
1197
1198 /*
1199 * Seek new wfp to end of tmpfile.
1200 */
1201 if(fseeko(wfp,0,SEEK_END) != 0){
1202 OWPError(ctx,OWPErrFATAL,errno,"fseeko(): %M");
1203 goto loop_err;
1204 }
1205 }
1206 }
1207 continue;
1208 loop_err:
1209 if(wfp != rfp){
1210 fclose(wfp);
1211 }
1212 goto err;
1213 }
1214
1215 clean_data:
1216
1217 /*
1218 * If two fp's were used, then the tmpfile needs to be copied
1219 * back to the original file.
1220 */
1221 if(wfp != rfp){
1222 if(I2CopyFile(OWPContextErrHandle(ctx),
1223 fileno(rfp),fileno(wfp),0) != 0){
1224 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1225 "_OWPCleanDataRecs: Unable to copy session data: I2CopyFile(): %M");
1226 fclose(wfp);
1227 goto err;
1228 }
1229 fclose(wfp);
1230 }
1231
1232 /*
1233 * Write NumDataRecords into file.
1234 * (This MUST be done before adding any skip records to the file
1235 * or there is a race condition where partial session results could
1236 * interpret skip records as data records!)
1237 */
1238 if( !OWPWriteDataHeaderNumDataRecs(ctx,rfp,num_recs)){
1239 goto err;
1240 }
1241
1242 return True;
1243
1244 err:
1245 OWPError(ctx, OWPErrFATAL,OWPErrUNKNOWN,
1246 "_OWPCleanDataRecs: Failed");
1247 return False;
1248 }
1249
1250 typedef struct _maxrecv_rec{
1251 OWPBoolean found;
1252 uint32_t index; /* count records (index in file) */
1253 uint32_t maxrecv; /* max seqno for received records */
1254 uint32_t badlost; /* seqno for *first* throw-away lost rec */
1255 uint32_t index_badlost; /* record index in file for badlost */
1256 } _maxrecv_rec;
1257
1258 static int
GetMaxRecv(OWPDataRec * rec,void * data)1259 GetMaxRecv(
1260 OWPDataRec *rec,
1261 void *data
1262 )
1263 {
1264 _maxrecv_rec *mrec = (_maxrecv_rec *)data;
1265
1266
1267 /*
1268 * If this record is not lost, update maxrecv
1269 */
1270 if(!OWPIsLostRecord(rec)){
1271 mrec->found = True;
1272 mrec->maxrecv = MAX(mrec->maxrecv,rec->seq_no);
1273 }
1274 /*
1275 * lost - if this is the first one greater than maxrecv, then
1276 * record the badlost.
1277 */
1278 else if((rec->seq_no > mrec->maxrecv) &&
1279 (mrec->badlost < mrec->maxrecv)){
1280 mrec->badlost = rec->seq_no;
1281 mrec->index_badlost = mrec->index;
1282 }
1283
1284 mrec->index++;
1285
1286 return 0;
1287 }
1288
1289 /*
1290 * Function: _OWPCleanUpSessions
1291 *
1292 * Description:
1293 * This function updates the "recv" side sessions.
1294 * The StopSessions message was not read in this case,
1295 * so it relies on the stoptime of the session to
1296 * clean things up. Also, it deletes all trailing
1297 * Missing packet records in the file.
1298 *
1299 * In Args:
1300 *
1301 * Out Args:
1302 *
1303 * Scope:
1304 * Returns:
1305 * Side Effect:
1306 */
1307 static OWPBoolean
_OWPCleanUpSessions(OWPControl cntrl,int * intr,OWPAcceptType * acceptval_ret,OWPTimeStamp stoptime)1308 _OWPCleanUpSessions(
1309 OWPControl cntrl,
1310 int *intr __attribute__((unused)),
1311 OWPAcceptType *acceptval_ret,
1312 OWPTimeStamp stoptime
1313 )
1314 {
1315 OWPAcceptType aval;
1316 OWPAcceptType *acceptval = &aval;
1317 OWPTestSession tptr;
1318
1319 if(acceptval_ret)
1320 acceptval = acceptval_ret;
1321 *acceptval = OWP_CNTRL_ACCEPT;
1322
1323 /*
1324 * Parse test session list and pull recv sessions into the receivers
1325 * list.
1326 */
1327 for(tptr = cntrl->tests;tptr;tptr = tptr->next){
1328 char sid_name[sizeof(OWPSID)*2+1];
1329 off_t toff;
1330 FILE *rfp,*wfp;
1331 _OWPSessionHeaderInitialRec fhdr;
1332 struct flock flk;
1333 uint32_t j;
1334 OWPDataRec rec;
1335 char rbuf[_OWP_MAXDATAREC_SIZE];
1336 uint32_t max_recv,num_recs;
1337
1338 if(!tptr->endpoint){
1339 OWPError(cntrl->ctx,OWPErrFATAL,EINVAL,
1340 "_OWPCleanUpSessions: no endpoint state!");
1341 goto err;
1342 }
1343
1344 /*
1345 * Only need to clean recv sessions.
1346 */
1347 if(tptr->endpoint->send){
1348 continue;
1349 }
1350
1351
1352 I2HexEncode(sid_name,tptr->sid,sizeof(OWPSID));
1353
1354 rfp = wfp = tptr->endpoint->datafile;
1355
1356 /*
1357 * Lock the data file for writing. This is needed so FetchSessions
1358 * coming in from other control connections will get consistent
1359 * information.
1360 */
1361 memset(&flk,0,sizeof(flk));
1362 flk.l_start = 0;
1363 flk.l_len = 0;
1364 flk.l_whence = SEEK_SET;
1365 flk.l_type = F_WRLCK;
1366
1367 if( fcntl(fileno(rfp), F_SETLKW, &flk) < 0){
1368 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1369 "_OWPCleanUpSessions: Unable to lock file sid(%s): %M",
1370 sid_name);
1371 goto err;
1372 }
1373
1374 max_recv = 0;
1375 if( !_OWPCleanDataRecs(cntrl->ctx,tptr,0xFFFFFFFF,stoptime,&max_recv,
1376 &toff)){
1377 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1378 "_OWPCleanUpSessions: Unable to clean data sid(%s): %M",
1379 sid_name);
1380 goto err;
1381 }
1382
1383 /*
1384 * Read needed file header info.
1385 */
1386 if(!_OWPReadDataHeaderInitial(cntrl->ctx,rfp,&fhdr)){
1387 goto err;
1388 }
1389
1390 /*
1391 * Because this is from a broken control connection, I
1392 * now need to clean out any records with an index greater than
1393 * the last received packet.
1394 *
1395 * If CleanDataRecs was unable to get a max index, then
1396 * this degrades to a full parse of the dataset for the max
1397 * 'found' index.
1398 */
1399 if(!max_recv){
1400 _maxrecv_rec maxrec;
1401
1402 memset(&maxrec,0,sizeof(maxrec));
1403 if(fseeko(rfp,fhdr.oset_datarecs,SEEK_SET) != 0){
1404 OWPError(cntrl->ctx,OWPErrFATAL,errno,"fseeko(): %M");
1405 goto err;
1406 }
1407 if(OWPParseRecords(cntrl->ctx,rfp,fhdr.num_datarecs,fhdr.version,
1408 GetMaxRecv,(void*)&maxrec) != OWPErrOK){
1409 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1410 "_OWPCleanUpSessions: GetMaxRecv failed");
1411 goto err;
1412 }
1413
1414 /*
1415 * If no records were actually received - truncate the
1416 * file to oset_datarecs and set num_datarecs to 0 and
1417 * be done.
1418 */
1419 if(!maxrec.found){
1420 if(ftruncate(fileno(rfp),fhdr.oset_datarecs) != 0){
1421 OWPError(cntrl->ctx,OWPErrFATAL,errno,"ftruncate(): %M");
1422 goto err;
1423 }
1424 if( !OWPWriteDataHeaderNumDataRecs(cntrl->ctx,rfp,0)){
1425 goto err;
1426 }
1427
1428 /* goto next session */
1429 continue;
1430 }
1431 max_recv = maxrec.maxrecv;
1432 toff = fhdr.oset_datarecs + (maxrec.index_badlost * fhdr.rec_size);
1433 }
1434
1435 /*
1436 * Advance fp to toff and remove all lost records with
1437 * index greater than max_recv from there to the end.
1438 */
1439 if(fseeko(rfp,toff,SEEK_SET) != 0){
1440 OWPError(cntrl->ctx,OWPErrFATAL,errno,"fseeko(): %M");
1441 goto err;
1442 }
1443
1444 num_recs = fhdr.num_datarecs;
1445 for(j=(toff - fhdr.oset_datarecs)/fhdr.rec_size;
1446 j<fhdr.num_datarecs;
1447 j++){
1448
1449 /*
1450 * Read the packet record from midI.
1451 */
1452 if(fread(rbuf,fhdr.rec_size,1,rfp) != 1){
1453 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1454 "fread(): Reading session file sid(%s): %M",
1455 sid_name);
1456 goto loop_err;
1457 }
1458 if(!_OWPDecodeDataRecord(fhdr.version,&rec,rbuf)){
1459 errno = EFTYPE;
1460 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1461 "_OWPCleanDataRecs: Invalid data record sid(%s)",
1462 sid_name);
1463 goto loop_err;
1464 }
1465
1466 /*
1467 * If seq_no is > max_recv then delete the packet
1468 */
1469 if(rec.seq_no > max_recv){
1470 num_recs--;
1471 /*
1472 * If wfp==rfp, then create another fp for wfp and point
1473 * it at the current record so it will be written over.
1474 */
1475 if(wfp == rfp){
1476 int newfd;
1477 char tmpfname[PATH_MAX];
1478 char *dname;
1479 char *tmpl = "owamp.XXXXXXXX";
1480
1481 /*
1482 * Need another file for bookkeeping... First determine
1483 * what dir to put it in.
1484 */
1485 dname = NULL;
1486 memset(tmpfname,'\0',sizeof(tmpfname));
1487
1488 /* put it in the same dir as session data if possible */
1489 if( strlen(tptr->endpoint->fname) > 0){
1490 strncpy(tmpfname,tptr->endpoint->fname,PATH_MAX);
1491 dname = dirname(tmpfname);
1492 }
1493
1494 /* otherwise use tmpdir */
1495 if( !dname){
1496 dname = getenv("TMPDIR");
1497 }
1498 if( !dname){
1499 dname = "/tmp";
1500 }
1501
1502 /* Make sure pathname will not over-run memory. */
1503 if(strlen(tmpl) + OWP_PATH_SEPARATOR_LEN + strlen(dname) >
1504 PATH_MAX){
1505 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1506 "_OWPCleanUpSessions: Unable to create temp file: Path Too Long");
1507 goto err;
1508 }
1509
1510 /* create template (fname) string for mkstemp */
1511 strcpy(tmpfname,dname);
1512 strcat(tmpfname,OWP_PATH_SEPARATOR);
1513 strcat(tmpfname,tmpl);
1514 if( (newfd = mkstemp(tmpfname)) < 0){
1515 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1516 "_OWPCleanUpSessions: mkstemp(%s): %M",
1517 tmpfname);
1518 goto err;
1519 }
1520
1521 /* immediately unlink - no need for a directory entry */
1522 if(unlink(tmpfname) != 0){
1523 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1524 "unlink(%s): %M",tmpfname);
1525 close(newfd);
1526 goto err;
1527 }
1528
1529 /*
1530 * Copy original file into tmpfile from the beginning
1531 * until just before the current record.
1532 */
1533 toff = fhdr.oset_datarecs + (j * fhdr.rec_size);
1534 if(I2CopyFile(OWPContextErrHandle(cntrl->ctx),
1535 newfd,fileno(rfp),toff) != 0){
1536 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1537 "_OWPCleanUpSessions: Unable to copy session data: I2CopyFile(): %M");
1538 close(newfd);
1539 goto err;
1540 }
1541
1542 if( !(wfp = fdopen(newfd,"r+b"))){
1543 OWPError(cntrl->ctx,OWPErrFATAL,errno,"fdopen(%d): %M",
1544 newfd);
1545 close(newfd);
1546 goto err;
1547 }
1548
1549 /*
1550 * Seek new wfp to end of tmpfile.
1551 */
1552 if(fseeko(wfp,0,SEEK_END) != 0){
1553 OWPError(cntrl->ctx,OWPErrFATAL,errno,"fseeko(): %M");
1554 goto loop_err;
1555 }
1556 }
1557 }
1558 /*
1559 * Otherwise, this is a good packet and it should be kept.
1560 */
1561 else{
1562 if(wfp != rfp){
1563 if(fwrite(rbuf,fhdr.rec_size,1,wfp) != 1){
1564 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1565 "fwrite(): Writing session file sid(%s): %M",
1566 sid_name);
1567 goto loop_err;
1568 }
1569 }
1570 }
1571 continue;
1572 loop_err:
1573 if(wfp != rfp){
1574 fclose(wfp);
1575 }
1576 goto err;
1577 }
1578
1579 /*
1580 * If two fp's were used, then the tmpfile needs to be copied
1581 * back to the original file.
1582 */
1583 if(wfp != rfp){
1584 if(I2CopyFile(OWPContextErrHandle(cntrl->ctx),
1585 fileno(rfp),fileno(wfp),0) != 0){
1586 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1587 "_OWPCleanUpSessions: Unable to copy session data: I2CopyFile(): %M");
1588 fclose(wfp);
1589 goto err;
1590 }
1591 fclose(wfp);
1592 }
1593
1594 /*
1595 * Write num_recs and "finished" into file.
1596 */
1597 if( !OWPWriteDataHeaderNumDataRecs(cntrl->ctx,rfp,num_recs)){
1598 goto err;
1599 }
1600
1601 /*
1602 * Session should still be marked as incomplete because
1603 * the StopSessions message information was not available.
1604 */
1605 if( !_OWPWriteDataHeaderFinished(cntrl->ctx,rfp,
1606 OWP_SESSION_FINISHED_INCOMPLETE,0)){
1607 goto err;
1608 }
1609
1610 flk.l_type = F_UNLCK;
1611 if( fcntl(fileno(rfp), F_SETLKW, &flk) < 0){
1612 OWPError(cntrl->ctx,OWPErrFATAL,errno,
1613 "_OWPCleanUpSessions: Unable to unlock file sid(%s): %M",
1614 sid_name);
1615 goto err;
1616 }
1617 }
1618
1619 return True;
1620
1621 err:
1622
1623 *acceptval = OWP_CNTRL_FAILURE;
1624 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1625 "_OWPCleanUpSessions: Failed");
1626 return False;
1627 }
1628
1629 /*
1630 * Function:
1631 *
1632 * Description:
1633 *
1634 * In Args:
1635 *
1636 * Out Args:
1637 *
1638 * Scope:
1639 * Returns:
1640 * Side Effect:
1641 */
1642 OWPErrSeverity
OWPStopSessions(OWPControl cntrl,int * retn_on_intr,OWPAcceptType * acceptval_ret)1643 OWPStopSessions(
1644 OWPControl cntrl,
1645 int *retn_on_intr,
1646 OWPAcceptType *acceptval_ret /* in/out */
1647 )
1648 {
1649 OWPErrSeverity err,err2=OWPErrOK;
1650 OWPRequestType msgtype;
1651 OWPAcceptType aval=OWP_CNTRL_ACCEPT;
1652 OWPAcceptType *acceptval=&aval;
1653 int ival=0;
1654 int *intr=&ival;
1655 uint32_t num_sessions=0;
1656 OWPTimeStamp stoptime;
1657 OWPBoolean readstop = True;
1658
1659 if(acceptval_ret){
1660 acceptval = acceptval_ret;
1661 }
1662
1663 if(retn_on_intr){
1664 intr = retn_on_intr;
1665 }
1666
1667 err = _OWPStopSendSessions(cntrl,acceptval,&num_sessions);
1668 err2 = MIN(err,err2);
1669 if(err2 < OWPErrWARNING){
1670 goto done;
1671 }
1672 err = _OWPWriteStopSessions(cntrl,intr,*acceptval,num_sessions);
1673 err2 = MIN(err,err2);
1674 if(err2 < OWPErrWARNING){
1675 readstop = False;
1676 goto clean_sessions;
1677 }
1678
1679 msgtype = OWPReadRequestType(cntrl,intr);
1680 switch(msgtype){
1681 case OWPReqStopSessions:
1682 break;
1683
1684 case OWPReqSockClose:
1685 case OWPReqSockIntr:
1686 readstop = False;
1687 break;
1688
1689 default:
1690 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
1691 "OWPStopSessions: Invalid protocol message received.");
1692 err2 = OWPErrFATAL;
1693 goto done;
1694 }
1695
1696 clean_sessions:
1697
1698 /*
1699 * Get time StopSessions was recieved and use that to
1700 * delete all packets with a presumed send time later
1701 * than stoptime-timeout. (As per section 3.8 owamp draft 14)
1702 */
1703 (void)OWPGetTimeOfDay(cntrl->ctx,&stoptime);
1704
1705 /*
1706 * Stop Recv side sessions now that we have received the
1707 * StopSessions message (even though it has not been completely
1708 * read yet).
1709 */
1710 err = _OWPStopRecvSessions(cntrl,acceptval);
1711 err2 = MIN(err,err2);
1712 if(err2 < OWPErrWARNING){
1713 goto done;
1714 }
1715
1716 /*
1717 * Now read the full StopSessions message.
1718 * This will take the NextSeq and SkipRecords and
1719 * put them in the recv session files as well.
1720 */
1721 if(readstop){
1722 err = _OWPReadStopSessions(cntrl,intr,acceptval,stoptime);
1723 err2 = MIN(err,err2);
1724 }
1725 else if(!_OWPCleanUpSessions(cntrl,intr,acceptval,stoptime)){
1726 err2 = OWPErrFATAL;
1727 }
1728
1729 done:
1730 /*
1731 * Free memory from sessions
1732 */
1733 while(cntrl->tests){
1734 err = _OWPTestSessionFree(cntrl->tests,*acceptval);
1735 err2 = MIN(err,err2);
1736 }
1737
1738 if(err2 < OWPErrWARNING){
1739 if(*acceptval == OWP_CNTRL_ACCEPT){
1740 *acceptval = OWP_CNTRL_FAILURE;
1741 }
1742 return _OWPFailControlSession(cntrl,err2);
1743 }
1744 else if(!readstop){
1745 return _OWPFailControlSession(cntrl,OWPErrFATAL);
1746 }
1747
1748 cntrl->state &= ~_OWPStateTest;
1749
1750 return err2;
1751 }
1752
1753 int
OWPStopSessionsWait(OWPControl cntrl,OWPNum64 * wake,int * retn_on_intr,OWPAcceptType * acceptval_ret,OWPErrSeverity * err_ret)1754 OWPStopSessionsWait(
1755 OWPControl cntrl,
1756 OWPNum64 *wake,
1757 int *retn_on_intr,
1758 OWPAcceptType *acceptval_ret,
1759 OWPErrSeverity *err_ret
1760 )
1761 {
1762 struct timeval reltime;
1763 struct timeval *waittime = NULL;
1764 fd_set readfds;
1765 fd_set exceptfds;
1766 int rc;
1767 int msgtype;
1768 OWPErrSeverity err2=OWPErrOK;
1769 OWPAcceptType aval;
1770 OWPAcceptType *acceptval=&aval;
1771 int ival=0;
1772 int *intr=&ival;
1773 uint32_t num_sessions=0;
1774 OWPTimeStamp stoptime;
1775 OWPBoolean readstop=True;
1776
1777 *err_ret = OWPErrOK;
1778 if(acceptval_ret){
1779 acceptval = acceptval_ret;
1780 }
1781 *acceptval = OWP_CNTRL_ACCEPT;
1782
1783 if(retn_on_intr){
1784 intr = retn_on_intr;
1785 }
1786
1787 if(!cntrl || cntrl->sockfd < 0){
1788 *err_ret = OWPErrFATAL;
1789 return -1;
1790 }
1791
1792 /*
1793 * If there are no active sessions, get the status and return.
1794 */
1795 if(!OWPSessionsActive(cntrl,acceptval) || (*acceptval)){
1796 /*
1797 * Sessions are complete - send StopSessions message.
1798 */
1799 *err_ret = OWPStopSessions(cntrl,intr,acceptval);
1800 return 0;
1801 }
1802
1803 if(wake){
1804 OWPTimeStamp currstamp;
1805 OWPNum64 wakenum;
1806
1807 if(!OWPGetTimeOfDay(cntrl->ctx,&currstamp)){
1808 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1809 "OWPGetTimeOfDay(): %M");
1810 return -1;
1811 }
1812
1813 if(OWPNum64Cmp(currstamp.owptime,*wake) < 0){
1814 wakenum = OWPNum64Sub(*wake,currstamp.owptime);
1815 OWPNum64ToTimeval(&reltime,wakenum);
1816 }
1817 else{
1818 tvalclear(&reltime);
1819 }
1820
1821 waittime = &reltime;
1822 }
1823
1824
1825 FD_ZERO(&readfds);
1826 FD_SET(cntrl->sockfd,&readfds);
1827 FD_ZERO(&exceptfds);
1828 FD_SET(cntrl->sockfd,&exceptfds);
1829 AGAIN:
1830 rc = select(cntrl->sockfd+1,&readfds,NULL,&exceptfds,waittime);
1831
1832 if(rc < 0){
1833 if(errno != EINTR){
1834 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1835 "select():%M");
1836 *err_ret = OWPErrFATAL;
1837 return -1;
1838 }
1839 if(waittime || *intr){
1840 return 2;
1841 }
1842
1843 /*
1844 * If there are tests still happening, and no tests have
1845 * ended in error - go back to select and wait for the
1846 * rest of the tests to complete.
1847 */
1848 if(OWPSessionsActive(cntrl,acceptval) && !*acceptval){
1849 goto AGAIN;
1850 }
1851
1852 /*
1853 * Sessions are complete - send StopSessions message.
1854 */
1855 *err_ret = OWPStopSessions(cntrl,intr,acceptval);
1856
1857 return 0;
1858 }
1859 if(rc == 0)
1860 return 1;
1861
1862 if(!FD_ISSET(cntrl->sockfd,&readfds) &&
1863 !FD_ISSET(cntrl->sockfd,&exceptfds)){
1864 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1865 "select():cntrl fd not ready?:%M");
1866 *err_ret = _OWPFailControlSession(cntrl,OWPErrFATAL);
1867 goto done;
1868 }
1869
1870 msgtype = OWPReadRequestType(cntrl,intr);
1871 switch(msgtype){
1872 case OWPReqStopSessions:
1873 break;
1874
1875 case OWPReqSockClose:
1876 case OWPReqSockIntr:
1877 /*
1878 * Go through all recv sessions and delete
1879 * all missing packet records *after* the last
1880 * good one. (section 3.8 of draft 14)
1881 * (readstop indicates the call of _OWPCleanUpSessions()
1882 * and that does the section 3.8 cleanup.)
1883 */
1884 readstop = False;
1885 break;
1886
1887 default:
1888 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
1889 "OWPStopSessionsWait: Invalid protocol message received.");
1890 *err_ret = OWPErrFATAL;
1891 goto done;
1892 }
1893
1894 /*
1895 * Get time StopSessions was recieved and use that to
1896 * delete all packets with a presumed send time later
1897 * than stoptime-timeout. (As per section 3.8 owamp draft 14)
1898 */
1899 (void)OWPGetTimeOfDay(cntrl->ctx,&stoptime);
1900
1901 /*
1902 * Stop Send sessions - the other side does not want more packets.
1903 * Do this first to be a "good" citizen.
1904 */
1905 err2 = _OWPStopSendSessions(cntrl,acceptval,&num_sessions);
1906 *err_ret = MIN(*err_ret,err2);
1907 if(*err_ret < OWPErrWARNING){
1908 goto done;
1909 }
1910
1911 /*
1912 * Stop Recv side sessions now that we have received the
1913 * StopSessions message (even though it has not been completely
1914 * read yet).
1915 */
1916 err2 = _OWPStopRecvSessions(cntrl,acceptval);
1917 *err_ret = MIN(*err_ret,err2);
1918 if(*err_ret < OWPErrWARNING){
1919 goto done;
1920 }
1921
1922 /*
1923 * Read the rest of the stop sessions message and complete
1924 * the recv side session files.
1925 */
1926 if(readstop){
1927 err2 = _OWPReadStopSessions(cntrl,intr,acceptval,stoptime);
1928 *err_ret = MIN(*err_ret,err2);
1929 }
1930 else if(!_OWPCleanUpSessions(cntrl,intr,acceptval,stoptime)){
1931 *err_ret = OWPErrFATAL;
1932 }
1933
1934 done:
1935
1936 if(*err_ret < OWPErrWARNING){
1937 *acceptval = OWP_CNTRL_FAILURE;
1938 }
1939
1940 /*
1941 * If errors are non-fatal (warning or better) then send the
1942 * stop sessions message.
1943 */
1944 if(readstop && (*err_ret >= OWPErrWARNING)){
1945 err2 = _OWPWriteStopSessions(cntrl,intr,*acceptval,num_sessions);
1946 *err_ret = MIN(*err_ret,err2);
1947 }
1948
1949 /*
1950 * Clean up memory.
1951 */
1952 while(cntrl->tests){
1953 err2 = _OWPTestSessionFree(cntrl->tests,*acceptval);
1954 *err_ret = MIN(*err_ret,err2);
1955 }
1956
1957 /*
1958 * If anything has failed along the way, report failure.
1959 */
1960 if(*err_ret < OWPErrWARNING){
1961 *acceptval = OWP_CNTRL_FAILURE;
1962 _OWPFailControlSession(cntrl,OWPErrFATAL);
1963 return -1;
1964 }
1965 else if(!readstop){
1966 _OWPFailControlSession(cntrl,OWPErrFATAL);
1967 return -1;
1968 }
1969
1970
1971 /*
1972 * Otherwise, report success.
1973 */
1974 cntrl->state &= ~_OWPStateTest;
1975
1976 return 0;
1977 }
1978
1979 static OWPSessionFinishedType
GetSessionFinishedType(OWPContext ctx,uint32_t val)1980 GetSessionFinishedType(
1981 OWPContext ctx,
1982 uint32_t val
1983 )
1984 {
1985 switch(val){
1986 case OWP_SESSION_FINISHED_ERROR:
1987 return OWP_SESSION_FINISHED_ERROR;
1988 case OWP_SESSION_FINISHED_NORMAL:
1989 return OWP_SESSION_FINISHED_NORMAL;
1990 case OWP_SESSION_FINISHED_INCOMPLETE:
1991 return OWP_SESSION_FINISHED_INCOMPLETE;
1992 default:
1993 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1994 "GetSessionFinishedType: Invalid val %u",val);
1995 return OWP_SESSION_FINISHED_ERROR;
1996 }
1997 }
1998
1999 /*
2000 * Functions for writing and reading headers. The format varies
2001 * according to the version. In all cases the files starts
2002 * with 4 bytes of magic number, 4 bytes of version, and
2003 * 8 bytes of total header length (version and header length
2004 * fields given in network byte order). The rest depends on
2005 * the version as follows:
2006 *
2007 * Version 0: nothing - data records follow "hdr length".
2008 * Version 2: Session Request as per version 5 of the protocol (use hdr len
2009 * to skip session request, or read it using the format described
2010 * below. (All values are in network byte order.)
2011 *
2012 * 0 format is as follows:
2013 *
2014 * 0 1 2 3
2015 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2016 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2017 * 00| "O" | "w" | "A" | \0 |
2018 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2019 * 04| Version |
2020 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2021 * 08| hdr length (unsigned 64bit) |
2022 * 12| |
2023 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2024 *
2025 * 2 format is as follows:
2026 *
2027 *
2028 * 0 1 2 3
2029 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2030 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2031 * 00| "O" | "w" | "A" | \0 |
2032 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2033 * 04| Version |
2034 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2035 * 08| hdr length (unsigned 64bit) |
2036 * 12| |
2037 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2038 * 16| Finished |
2039 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2040 * 20| |
2041 * ... TestRequestPreamble (protocol.c) ...
2042 * 128| |
2043 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2044 * 132| |
2045 * 136| Slot(1) definitions (16 octets each) |
2046 * 140| |
2047 * 144| |
2048 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2049 * 148:(148+(16*(nslots-1)) (16 octets for each additional slot)
2050 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2051 * | |
2052 * | Zero Integrity Padding (16 octets) |
2053 * | |
2054 * | |
2055 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2056 *
2057 * Then individual packet records start. (hdr_len should point to here.)
2058 *
2059 * 0 1 2 3
2060 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2061 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2062 * 00| Sequence Number |
2063 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2064 * 04| |
2065 * 08| Send Timestamp |
2066 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2067 * 12| Send Error Estimate | |
2068 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
2069 * 16| Recv Timestamp |
2070 * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2071 * 20| | Recv Error Estimate |
2072 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2073 *
2074 * Version 3: Session Request as per version 12 of the protocol (use hdr len
2075 * to skip the file header which includes file specific fields, the
2076 * session request and possibly the skip records. (skip records
2077 * can be between the session request and the packet records or
2078 * follow everything.
2079 *
2080 * 0 1 2 3
2081 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
2082 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2083 * 00| "O" | "w" | "A" | \0 |
2084 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2085 * 04| Version |
2086 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2087 * 08| Finished |
2088 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2089 * 12| Next Seqno |
2090 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2091 * 16| Number of Skip Ranges |
2092 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2093 * 20| Number of Records |
2094 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2095 * 24| oset to Skip Ranges |
2096 * 28| |
2097 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2098 * 32| oset to Records |
2099 * 36| |
2100 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2101 * 40| |
2102 * ... TestRequestPreamble (protocol.c) ...
2103 * 148| |
2104 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2105 * 152| |
2106 * 156| Slot(1) definitions (16 octets each) |
2107 * 160| |
2108 * 164| |
2109 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2110 * 168:(168+(16*(nslots-1))) (16 octets for each additional slot)
2111 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2112 * | |
2113 * | Integrity Zero Padding (16 octets) |
2114 * | |
2115 * | |
2116 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2117 *
2118 * Then individual packet records or skip records start. It does not matter
2119 * which is first. The Num Skip Records and Num Data Records fields are
2120 * used to determine how long these ranges will be.
2121 *
2122 * The format for individual packet records is documented in the
2123 * header for the _OWPDecodeDataRecord function which should be used
2124 * to fetch them.
2125 *
2126 * The format for individual skip records is documented in the
2127 * header for the _OWPDecodeSkipRecord function which should be used
2128 * to fetch them.
2129 *
2130 * If Number of Skip Ranges == 0, then the value of oset to Skip Ranges
2131 * can remain nil.
2132 *
2133 * The API is setup to work from two points of view.
2134 *
2135 * Endpoint Reciever:
2136 * Packets come in and it will not know about skips until
2137 * after all data records are done. If WriteDataHeader is called with
2138 * num_skiprecs == 0, then the file will be setup in this mode. Then
2139 * the WriteDataHeaderNumDataRecs function will need to be called to
2140 * set that field in the file data records are complete. When the
2141 * StopSessions message comes across it will include the skip_recs and
2142 * the WriteDataHeaaderNumSkipRecs function can be called to add skip
2143 * records. This function will set the skip_oset if skip_oset is
2144 * currently nil in addition to the num_skips. Then skip records
2145 * are written until complete. The num_datarecs field MUST be set
2146 * before the WriteDataHeaderNumskips can be called.
2147 *
2148 * FetchClient:
2149 * Entire session is retrieved. Skip records come before data records
2150 * in a Fetch response so WriteDataHeader is called with num_skiprecs
2151 * set. (If num_skiprecs is 0, then oset_skips will be initialized
2152 * to null.) SkipRecords can be written until they are complete. Then
2153 * datarecords can be written. In this case the number of records will
2154 * also have already been set with WriteDataHeader.
2155 * WriteDataHeaderNumSkipRecs is not valid to be called for a file that
2156 * is initialized this way but WriteDataHeaderNumDataRecs
2157 *
2158 */
2159
2160 /*
2161 * Function: _OWPReadDataHeaderInitial
2162 *
2163 * Description:
2164 * This function reads the initial file fields and also verifies the
2165 * file is valid.
2166 *
2167 *
2168 * In Args:
2169 *
2170 * Out Args:
2171 *
2172 * Scope:
2173 * Returns:
2174 * Side Effect:
2175 * fp will be advanced to the start of the TestRequestPreamble
2176 * for version >= 2 files. For version <=1 files fp will be
2177 * at the begining of data records.
2178 */
2179 static uint8_t owp_magic[] = _OWP_MAGIC_FILETYPE;
2180 OWPBoolean
_OWPReadDataHeaderInitial(OWPContext ctx,FILE * fp,_OWPSessionHeaderInitial phdr)2181 _OWPReadDataHeaderInitial(
2182 OWPContext ctx,
2183 FILE *fp,
2184 _OWPSessionHeaderInitial phdr
2185 )
2186 {
2187 uint8_t read_magic[sizeof(owp_magic)];
2188 int err;
2189 uint64_t oset;
2190 uint32_t finished=0;
2191
2192 /*
2193 * Initialize private file header record.
2194 */
2195 memset(phdr,0,sizeof(*phdr));
2196
2197 /*
2198 * Stat the file to get the size and check that it is really there.
2199 */
2200 if(fstat(fileno(fp),&phdr->sbuf) < 0){
2201 err = errno;
2202 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fstat(): %M");
2203 errno = err;
2204 return False;
2205 }
2206
2207 /*
2208 * Position fp to beginning of file.
2209 */
2210 if(fseeko(fp,0,SEEK_SET)){
2211 err = errno;
2212 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2213 errno = err;
2214 return False;
2215 }
2216
2217 /*
2218 * File must be at least as big as the initial header information.
2219 * 16 bytes is magic+version+hdr_length which is the minimum
2220 * size of any valid owp file. (version 0 files)
2221 */
2222 if(phdr->sbuf.st_size < (off_t)16){
2223 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2224 "_OWPReadDataHeaderInitial: Invalid owp file");
2225 /*
2226 * TODO: Check validity of this errno... May need to
2227 * use ENOSYS...
2228 */
2229 errno = EFTYPE;
2230 return False;
2231 }
2232
2233 /*
2234 * Read and check "magic".
2235 * 4 bytes
2236 */
2237 if(fread(&read_magic[0], 1, 4, fp) != 4){
2238 err = errno;
2239 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2240 errno = err;
2241 return False;
2242 }
2243 if(memcmp(read_magic,owp_magic,4) != 0){
2244 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2245 "_OWPReadDataHeaderInitial: Invalid owp file:wrong magic");
2246 /*
2247 * TODO: Check validity of this errno... May need to
2248 * use ENOSYS...
2249 */
2250 errno = EFTYPE;
2251 return False;
2252 }
2253
2254 /*
2255 * Get the file "version".
2256 * 4 byte "network long" quantity
2257 */
2258 if(fread(&phdr->version, 1, 4, fp) != 4){
2259 err = errno;
2260 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2261 errno = err;
2262 return False;
2263 }
2264 phdr->version = ntohl(phdr->version);
2265
2266 /*
2267 * Currently it supports 0 and 2 and 3.
2268 */
2269 phdr->header = True;
2270 switch(phdr->version){
2271 case 0:
2272 phdr->header = False;
2273 case 2:
2274 if(fread(&oset, 1, 8, fp) != 8){
2275 err = errno;
2276 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2277 errno = err;
2278 return False;
2279 }
2280 oset = ntohll(oset);
2281 phdr->hdr_len = (off_t)oset;
2282 if(oset != (uint64_t)phdr->hdr_len){
2283 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2284 "OWPReadDataHeaderInitial: Unable to represent file offset (%ull)",
2285 oset);
2286 return False;
2287 }
2288 phdr->rec_size = _OWP_DATARECV2_SIZE;
2289
2290 break;
2291 case 3:
2292 phdr->rec_size = _OWP_DATARECV3_SIZE;
2293 break;
2294 default:
2295 OWPError(ctx,OWPErrFATAL,EINVAL,
2296 "_OWPReadDataHeaderInitial: Invalid file version (%ul)",
2297 phdr->version);
2298 return False;
2299 }
2300
2301 if(phdr->version == 0)
2302 return True;
2303
2304 /*
2305 * Finished
2306 */
2307 if(fread(&finished, 1, 4, fp) != 4){
2308 err = errno;
2309 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2310 errno = err;
2311 return False;
2312 }
2313 phdr->finished = GetSessionFinishedType(ctx,ntohl(finished));
2314
2315 if(phdr->version < 3){
2316 phdr->oset_skiprecs = NULL;
2317 phdr->num_skiprecs = 0;
2318 phdr->oset_datarecs = phdr->hdr_len;
2319 phdr->num_datarecs = (phdr->sbuf.st_size - phdr->hdr_len)/
2320 phdr->rec_size;
2321 phdr->next_seqno = 0;
2322
2323 return True;
2324 }
2325
2326 /*
2327 * Next Seqno
2328 */
2329 if(fread(&phdr->next_seqno, 1, 4, fp) != 4){
2330 err = errno;
2331 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2332 errno = err;
2333 return False;
2334 }
2335 phdr->next_seqno = ntohl(phdr->next_seqno);
2336
2337 /*
2338 * Num Skips
2339 */
2340 if(fread(&phdr->num_skiprecs, 1, 4, fp) != 4){
2341 err = errno;
2342 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2343 errno = err;
2344 return False;
2345 }
2346 phdr->num_skiprecs = ntohl(phdr->num_skiprecs);
2347
2348 /*
2349 * Num Datarecs
2350 */
2351 if(fread(&phdr->num_datarecs, 1, 4, fp) != 4){
2352 err = errno;
2353 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2354 errno = err;
2355 return False;
2356 }
2357 phdr->num_datarecs = ntohl(phdr->num_datarecs);
2358
2359 /*
2360 * Skips oset
2361 */
2362 if(fread(&oset, 1, 8, fp) != 8){
2363 err = errno;
2364 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2365 errno = err;
2366 return False;
2367 }
2368 oset = ntohll(oset);
2369 phdr->oset_skiprecs = (off_t)oset;
2370 if(oset != (uint64_t)phdr->oset_skiprecs){
2371 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2372 "OWPReadDataHeaderInitial: Unable to represent file offset (%ull)",
2373 oset);
2374 return False;
2375 }
2376
2377 /*
2378 * Datarecs oset
2379 */
2380 if(fread(&oset, 1, 8, fp) != 8){
2381 err = errno;
2382 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fread(): %M");
2383 errno = err;
2384 return False;
2385 }
2386 oset = ntohll(oset);
2387 phdr->oset_datarecs = (off_t)oset;
2388 if(oset != (uint64_t)phdr->oset_datarecs){
2389 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2390 "OWPReadDataHeaderInitial: Unable to represent file offset (%ull)",
2391 oset);
2392 return False;
2393 }
2394 phdr->hdr_len = phdr->oset_datarecs;
2395
2396 return True;
2397 }
2398
2399 /*
2400 * Function: _OWPWriteDataHeaderFinished
2401 *
2402 * Description:
2403 * Write a new "finished" word into the file. This function seeks to
2404 * the correct offset for a version 3 file.
2405 *
2406 * In Args:
2407 *
2408 * Out Args:
2409 *
2410 * Scope:
2411 * Returns:
2412 * Side Effect:
2413 */
2414 OWPBoolean
_OWPWriteDataHeaderFinished(OWPContext ctx,FILE * fp,OWPSessionFinishedType finished,uint32_t next_seqno)2415 _OWPWriteDataHeaderFinished(
2416 OWPContext ctx,
2417 FILE *fp,
2418 OWPSessionFinishedType finished,
2419 uint32_t next_seqno
2420 )
2421 {
2422 int err;
2423 uint32_t finword;
2424
2425 if(finished > 2){
2426 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2427 "_OWPWriteDataHeaderFinished: Invalid \"finished\"");
2428 return False;
2429 }
2430
2431 /*
2432 * seek to finished word.
2433 */
2434 if(fseeko(fp,8,SEEK_SET)){
2435 err = errno;
2436 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2437 errno = err;
2438 return False;
2439 }
2440
2441 /*
2442 * Write
2443 */
2444 finword = htonl((uint32_t)finished);
2445 if(fwrite(&finword,1,sizeof(finword),fp) != 4){
2446 err = errno;
2447 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2448 errno = err;
2449 return False;
2450 }
2451
2452 next_seqno = htonl(next_seqno);
2453 if(fwrite(&next_seqno,1,sizeof(next_seqno),fp) != 4){
2454 err = errno;
2455 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2456 errno = err;
2457 return False;
2458 }
2459
2460 if(fflush(fp) != 0){
2461 err = errno;
2462 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fflush(): %M");
2463 errno = err;
2464 return False;
2465 }
2466
2467 return True;
2468 }
2469
2470 /*
2471 * Function: OWPWriteDataHeaderNumSkipRecs
2472 *
2473 * Description:
2474 * Sets the num_skips field and the oset_skips
2475 * field. oset_datarecs and num_datarecs
2476 * MUST be set prior to this call. (Either by calling
2477 * WriteDataHeader with num_datarecs or by calling
2478 * WriteDataHeaderRecords.)
2479 *
2480 * This function should only be called if skip records are
2481 * being placed after datarecs, and then only after the
2482 * number of datarecs has been fixed. If skip records are
2483 * first in the file these fields MUST be initalized with
2484 * the proper num_skips.
2485 *
2486 * In Args:
2487 *
2488 * Out Args:
2489 *
2490 * Scope:
2491 * Returns:
2492 * Side Effect:
2493 * fp will be advanced to an undefined offset.
2494 */
2495 OWPBoolean
OWPWriteDataHeaderNumSkipRecs(OWPContext ctx,FILE * fp,uint32_t num_skiprecs)2496 OWPWriteDataHeaderNumSkipRecs(
2497 OWPContext ctx,
2498 FILE *fp,
2499 uint32_t num_skiprecs
2500 )
2501 {
2502 _OWPSessionHeaderInitialRec phrec;
2503 uint32_t n32;
2504 uint64_t n64;
2505 int err;
2506
2507 if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
2508 return False;
2509 }
2510
2511 /*
2512 * Files before version 3 don't have skips.
2513 */
2514 if(phrec.version < 3){
2515 OWPError(ctx,OWPErrFATAL,EINVAL,
2516 "_OWPWriteDataHeaderNumSkipRecs: Invalid file version (%ul)",
2517 phrec.version);
2518 errno = EINVAL;
2519 return False;
2520 }
2521
2522 /*
2523 * This function should not be called on a file that already has
2524 * initialized num_skiprecs and oset_skiprecs.
2525 */
2526 if(phrec.num_skiprecs || phrec.oset_skiprecs){
2527 OWPError(ctx,OWPErrFATAL,EINVAL,
2528 "_OWPWriteDataHeaderNumSkipRecs: Number skips already defined");
2529 errno = EINVAL;
2530 return False;
2531 }
2532
2533 /*
2534 * Position fp to num_skiprecs field.
2535 */
2536 if(fseeko(fp,16,SEEK_SET)){
2537 err = errno;
2538 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2539 errno = err;
2540 return False;
2541 }
2542
2543 /*
2544 * write num_skiprecs
2545 */
2546 n32 = htonl(num_skiprecs);
2547 if(fwrite(&n32, 1, sizeof(n32), fp) != sizeof(n32)){
2548 err = errno;
2549 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2550 errno = err;
2551 return False;
2552 }
2553
2554 /*
2555 * Position fp to oset_skiprecs field.
2556 */
2557 if(fseeko(fp,24,SEEK_SET)){
2558 err = errno;
2559 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2560 errno = err;
2561 return False;
2562 }
2563
2564 /*
2565 * Convert off_t oset_skiprecs to network ordered uint64_t
2566 */
2567 phrec.oset_skiprecs = phrec.oset_datarecs +
2568 (phrec.rec_size * phrec.num_datarecs);
2569 n64 = (uint64_t)phrec.oset_skiprecs;
2570 if(phrec.oset_skiprecs != (off_t)n64){
2571 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2572 "_OWPWriteDataHeaderNumSkipRecs: Unable to represet file offset (%ull)",
2573 phrec.oset_skiprecs);
2574 return False;
2575 }
2576 n64 = htonll(n64);
2577
2578 /*
2579 * write oset_skiprecs
2580 */
2581 if(fwrite(&n64, 1, sizeof(n64), fp) != sizeof(n64)){
2582
2583 err = errno;
2584 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2585 errno = err;
2586 return False;
2587 }
2588
2589 return True;
2590 }
2591
2592 /*
2593 * Function: OWPWriteDataHeaderNumDataRecs
2594 *
2595 * Description:
2596 * Sets the num_datarecs field.
2597 * If oset_skiprecs is nil, this function sets that to
2598 * just beyond the data records.
2599 *
2600 * In Args:
2601 *
2602 * Out Args:
2603 *
2604 * Scope:
2605 * Returns:
2606 * Side Effect:
2607 */
2608 OWPBoolean
OWPWriteDataHeaderNumDataRecs(OWPContext ctx,FILE * fp,uint32_t num_datarecs)2609 OWPWriteDataHeaderNumDataRecs(
2610 OWPContext ctx,
2611 FILE *fp,
2612 uint32_t num_datarecs
2613 )
2614 {
2615 _OWPSessionHeaderInitialRec phrec;
2616 uint32_t n32;
2617 int err;
2618
2619 if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
2620 return False;
2621 }
2622
2623 /*
2624 * Files before version 3 not supported for writing.
2625 */
2626 if(phrec.version < 3){
2627 OWPError(ctx,OWPErrFATAL,EINVAL,
2628 "_OWPWriteDataHeaderNumDataRecs: Invalid file version (%ul)",
2629 phrec.version);
2630 errno = EINVAL;
2631 return False;
2632 }
2633
2634 /*
2635 * This function should not be called on a file that has
2636 * initialized oset_skiprecs to a greater offset than oset_datarecs.
2637 */
2638 if(phrec.oset_datarecs < phrec.oset_skiprecs){
2639 OWPError(ctx,OWPErrFATAL,EINVAL,
2640 "_OWPWriteDataHeaderNumDataRecs: Can't change number of datarecs.");
2641 errno = EINVAL;
2642 return False;
2643 }
2644
2645 /*
2646 * Position fp to num_datarecs field.
2647 */
2648 if(fseeko(fp,20,SEEK_SET)){
2649 err = errno;
2650 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
2651 errno = err;
2652 return False;
2653 }
2654
2655 /*
2656 * write num_datarecs
2657 */
2658 n32 = htonl(num_datarecs);
2659 if(fwrite(&n32, 1, sizeof(n32), fp) != sizeof(n32)){
2660
2661 err = errno;
2662 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fwrite(): %M");
2663 errno = err;
2664 return False;
2665 }
2666
2667
2668 return True;
2669 }
2670
2671 /*
2672 * Function: OWPWriteDataHeader
2673 *
2674 * Description:
2675 * Write data header to the file.
2676 *
2677 * The fp is left pointing just past the IZP ready for either skip records
2678 * or data records.
2679 *
2680 * If num_skiprecs is non-zero, the file is configured for skip records
2681 * to come before data records and oset_skiprecs and oset_datarecs is
2682 * initialized in the file and returned in hdr.
2683 *
2684 * If num_skiprecs is zero, the file is configured for data records to
2685 * come before skip records. oset_datarecs will be set to just beyond
2686 * the header. If num_datarecs is zero, oset_skiprecs will be nil. If
2687 * num_datarecs is set, oset_skiprecs will be initialized as well.
2688 *
2689 *
2690 * In Args:
2691 *
2692 * Out Args:
2693 *
2694 * Scope:
2695 * Returns:
2696 * Side Effect:
2697 * This function does not catch signals in the I/O with the
2698 * file. If there is a signal - the fwrite will fail and this
2699 * function will fail. The caller is responsible for checking any
2700 * signal state.
2701 */
2702 OWPBoolean
OWPWriteDataHeader(OWPContext ctx,FILE * fp,OWPSessionHeader hdr)2703 OWPWriteDataHeader(
2704 OWPContext ctx,
2705 FILE *fp,
2706 OWPSessionHeader hdr
2707 )
2708 {
2709 uint32_t ver;
2710 uint32_t finished = OWP_SESSION_FINISHED_INCOMPLETE;
2711 uint64_t oset;
2712 uint64_t skip_oset = 0;
2713 uint64_t data_oset;
2714 off_t oset_off;
2715
2716 /* use uint32_t for proper alignment */
2717 uint32_t msg[_OWP_TEST_REQUEST_PREAMBLE_SIZE/sizeof(uint32_t)];
2718 uint32_t len = sizeof(msg);
2719 uint32_t i;
2720 uint32_t net32;
2721 uint64_t net64;
2722
2723 if(!hdr){
2724 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2725 "OWPWriteDataHeader: No hdr data specified");
2726 return False;
2727 }
2728
2729 /*
2730 * encode test_spec early so failure is detected early.
2731 */
2732 if((_OWPEncodeTestRequestPreamble(ctx,msg,&len,
2733 (struct sockaddr*)&hdr->addr_sender,
2734 (struct sockaddr*)&hdr->addr_receiver,
2735 hdr->conf_sender,hdr->conf_receiver,
2736 hdr->sid,&hdr->test_spec) != 0) || !len){
2737 return False;
2738 }
2739 ver = htonl(3);
2740
2741 /*
2742 * Compute the offset to the end of the "header" information. Either
2743 * the data records, or the skip records depending upon which comes
2744 * first:
2745 * MAGIC +
2746 * Version +
2747 * Finished +
2748 * NextSeqno +
2749 * NumSkips +
2750 * NumRecs +
2751 * OsetSkips +
2752 * OsetRecs +
2753 * TestRequestPramble +
2754 * Slots
2755 */
2756 oset = sizeof(owp_magic) +
2757 sizeof(ver) +
2758 sizeof(finished) +
2759 sizeof(hdr->next_seqno) +
2760 sizeof(hdr->num_skiprecs) +
2761 sizeof(hdr->num_datarecs) +
2762 sizeof(oset)+
2763 sizeof(oset)+
2764 len +
2765 16*(hdr->test_spec.nslots+1);
2766
2767 oset_off = (off_t)oset;
2768 if(oset != (uint64_t)oset_off){
2769 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2770 "OWPWriteDataHeader: Header too large for format representation (%"
2771 PRIu64 ")", oset);
2772 return False;
2773 }
2774
2775
2776 /*
2777 * write magic
2778 */
2779 if(fwrite(owp_magic, 1, sizeof(owp_magic), fp) != sizeof(owp_magic)){
2780 return False;
2781 }
2782
2783 /*
2784 * write version
2785 */
2786 if(fwrite(&ver, 1, sizeof(ver), fp) != sizeof(ver)){
2787 return False;
2788 }
2789
2790 /*
2791 * write finished
2792 */
2793 if(hdr){
2794 switch(hdr->finished){
2795 case 0:
2796 case 1:
2797 finished = hdr->finished;
2798 break;
2799 default:
2800 break;
2801 }
2802 }
2803 net32 = htonl(finished);
2804 if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2805 return False;
2806 }
2807
2808 /*
2809 * Rest of "fixed" header.
2810 */
2811 net32 = htonl(hdr->next_seqno);
2812 if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2813 return False;
2814 }
2815 net32 = htonl(hdr->num_skiprecs);
2816 if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2817 return False;
2818 }
2819 net32 = htonl(hdr->num_datarecs);
2820 if(fwrite(&net32,1,sizeof(net32),fp) != sizeof(net32)){
2821 return False;
2822 }
2823
2824 /*
2825 * write osets
2826 *
2827 * This logic puts the skip records first in the file if num_skiprecs
2828 * is set, and puts datarecords first otherwise. If data records are
2829 * first, the number of datarecs MUST be set in the file before any
2830 * skip records can be written.
2831 *
2832 */
2833 if(hdr->num_skiprecs){
2834 skip_oset = oset;
2835 data_oset = oset + (hdr->num_skiprecs * _OWP_SKIPREC_SIZE);
2836 }
2837 else{
2838 data_oset = oset;
2839
2840 if(hdr->num_datarecs){
2841 skip_oset = oset + (hdr->num_datarecs * hdr->rec_size);
2842 }
2843 }
2844
2845 net64 = htonll(skip_oset);
2846 if(fwrite(&net64,1,sizeof(net64),fp)!=sizeof(net64)){
2847 return False;
2848 }
2849
2850 net64 = htonll(data_oset);
2851 if(fwrite(&net64,1,sizeof(net64),fp)!=sizeof(net64)){
2852 return False;
2853 }
2854
2855 /*
2856 * write TestRequest preamble
2857 */
2858 if(fwrite(msg,1,len,fp) != len){
2859 return False;
2860 }
2861
2862 /*
2863 * write slots
2864 */
2865 for(i=0;i<hdr->test_spec.nslots;i++){
2866 /*
2867 * Each slot is one block (16 bytes)
2868 */
2869 if(_OWPEncodeSlot(msg,&hdr->test_spec.slots[i]) !=
2870 OWPErrOK){
2871 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
2872 "OWPWriteDataHeader: Invalid slot record");
2873 return False;
2874 }
2875 if(fwrite(msg,1,16,fp) != 16){
2876 return False;
2877 }
2878
2879 }
2880
2881 /*
2882 * write 16 Zero bytes in place of HMAC
2883 */
2884 memset(msg,0,16);
2885 if(fwrite(msg,1,16,fp) != 16){
2886 return False;
2887 }
2888
2889 fflush(fp);
2890 return True;
2891 }
2892
2893 /*
2894 * Function: OWPWriteDataRecord
2895 *
2896 * Description:
2897 * Write a single data record described by rec to file fp.
2898 *
2899 * In Args:
2900 *
2901 * Out Args:
2902 *
2903 * Scope:
2904 * Returns:
2905 * Side Effect:
2906 */
2907 OWPBoolean
OWPWriteDataRecord(OWPContext ctx,FILE * fp,OWPDataRec * rec)2908 OWPWriteDataRecord(
2909 OWPContext ctx,
2910 FILE *fp,
2911 OWPDataRec *rec
2912 )
2913 {
2914 char buf[_OWP_DATAREC_SIZE];
2915
2916 if(!_OWPEncodeDataRecord(buf,rec)){
2917 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2918 "OWPWriteDataRecord: Unable to encode data record");
2919 return False;
2920 }
2921
2922 /*
2923 * write data record
2924 */
2925 if(fwrite(buf,1,_OWP_DATAREC_SIZE,fp) != _OWP_DATAREC_SIZE){
2926 OWPError(ctx,OWPErrFATAL,errno,
2927 "OWPWriteDataRecord: fwrite(): %M");
2928 return False;
2929 }
2930
2931 return True;
2932 }
2933
2934
2935 /*
2936 * Function: OWPReadDataHeader
2937 *
2938 * Description:
2939 * Version 0:
2940 * nothing - data records follow.
2941 * Version 2:
2942 * Session Request as per version 5 of the protocol
2943 * This function does NOT read the slots into the hdr_ret->test_spec.
2944 * A separate function OWPReadDataHeaderSlots has been provided to do
2945 * that. (Memory for the slots must be provided by the caller.)
2946 * Version 3:
2947 * Same as 2, but api modifed to not return hdr_len as a field.
2948 * hdr_ret is now REQUIRED to be filled in, and the oset to data
2949 * and/or skip records can be retrieved from fields in that record.
2950 *
2951 *
2952 * In Args:
2953 *
2954 * Out Args:
2955 *
2956 * Scope:
2957 * Returns:
2958 * Side Effect:
2959 */
2960 uint32_t
OWPReadDataHeader(OWPContext ctx,FILE * fp,OWPSessionHeader hdr_ret)2961 OWPReadDataHeader(
2962 OWPContext ctx,
2963 FILE *fp,
2964 OWPSessionHeader hdr_ret
2965 )
2966 {
2967 _OWPSessionHeaderInitialRec phrec;
2968 int err;
2969
2970 /* buffer for TestRequest 32 bit aligned */
2971 uint32_t msg[_OWP_TEST_REQUEST_PREAMBLE_SIZE / sizeof(uint32_t)];
2972
2973 hdr_ret->header = 0;
2974
2975 if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
2976 return 0;
2977 }
2978
2979 hdr_ret->version = phrec.version;
2980 hdr_ret->sbuf = phrec.sbuf;
2981 hdr_ret->rec_size = phrec.rec_size;
2982
2983 /*
2984 * Decode the header if present(version 2).
2985 */
2986 if(phrec.version >= 2){
2987
2988 hdr_ret->finished = phrec.finished;
2989
2990 /*
2991 * read TestRequestPreamble
2992 */
2993 if(fread(msg,1,_OWP_TEST_REQUEST_PREAMBLE_SIZE,fp) !=
2994 _OWP_TEST_REQUEST_PREAMBLE_SIZE){
2995 err = errno;
2996 OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
2997 errno = err;
2998 return 0;
2999 }
3000
3001 hdr_ret->addr_len = sizeof(hdr_ret->addr_sender);
3002 /*
3003 * Now decode it into the hdr_ret variable.
3004 */
3005 if(_OWPDecodeTestRequestPreamble(ctx,False,msg,
3006 _OWP_TEST_REQUEST_PREAMBLE_SIZE,
3007 (struct sockaddr*)&hdr_ret->addr_sender,
3008 (struct sockaddr*)&hdr_ret->addr_receiver,
3009 &hdr_ret->addr_len,&hdr_ret->ipvn,
3010 &hdr_ret->conf_sender,&hdr_ret->conf_receiver,
3011 hdr_ret->sid,&hdr_ret->test_spec) != OWPErrOK){
3012 /*
3013 * TODO: Check validity of this errno... May need to
3014 * use ENOSYS...
3015 */
3016 errno = EFTYPE;
3017 return 0;
3018 }
3019
3020 hdr_ret->header = True;
3021 }
3022
3023 /*
3024 * Forward fp to data records.
3025 */
3026 if(fseeko(fp,phrec.hdr_len,SEEK_SET)){
3027 err = errno;
3028 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
3029 errno = err;
3030 return 0;
3031 }
3032
3033 /*
3034 * Make sure num_datarecs is not larger than the file allows.
3035 */
3036 if(phrec.num_datarecs > ((phrec.sbuf.st_size - phrec.hdr_len)/
3037 hdr_ret->rec_size)){
3038 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3039 "OWPReadDataHeader: num_datarecs field larger than filesize.");
3040 return 0;
3041 }
3042
3043 hdr_ret->next_seqno = phrec.next_seqno;
3044 hdr_ret->num_skiprecs = phrec.num_skiprecs;
3045 hdr_ret->oset_skiprecs = phrec.oset_skiprecs;
3046 hdr_ret->oset_datarecs = phrec.oset_datarecs;
3047 if(phrec.finished != OWP_SESSION_FINISHED_NORMAL){
3048 hdr_ret->num_datarecs = (phrec.sbuf.st_size - phrec.hdr_len)/
3049 hdr_ret->rec_size;
3050 }
3051 else{
3052 hdr_ret->num_datarecs = phrec.num_datarecs;
3053 }
3054
3055 return hdr_ret->num_datarecs;
3056 }
3057
3058 /*
3059 * Function: OWPReadDataHeaderSlots
3060 *
3061 * Description:
3062 * This function will read all the slot records out of the
3063 * file fp. slots is assumed to be an array of OWPSlot records of
3064 * length nslots.
3065 *
3066 * This function will position the fp to the beginning of the data
3067 * records.
3068 *
3069 * In Args:
3070 *
3071 * Out Args:
3072 *
3073 * Scope:
3074 * Returns:
3075 * Side Effect:
3076 */
3077 OWPBoolean
OWPReadDataHeaderSlots(OWPContext ctx,FILE * fp,uint32_t nslots,OWPSlot * slots)3078 OWPReadDataHeaderSlots(
3079 OWPContext ctx,
3080 FILE *fp,
3081 uint32_t nslots,
3082 OWPSlot *slots
3083 )
3084 {
3085 int err;
3086 _OWPSessionHeaderInitialRec phrec;
3087 uint32_t fileslots;
3088 uint32_t i;
3089 off_t slot_off;
3090 off_t hdr_off;
3091
3092 /* buffer for Slots 32 bit aligned */
3093 uint32_t msg[16/sizeof(uint32_t)];
3094 uint32_t zero[16/sizeof(uint32_t)];
3095
3096 /*
3097 * validate array.
3098 */
3099 assert(slots);
3100
3101 /*
3102 * Stat the file and get the "initial" fields from the header.
3103 */
3104 if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
3105 return False;
3106 }
3107
3108 /*
3109 * this function is currently only supported for version >=2
3110 */
3111 if(phrec.version < 2){
3112 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3113 "OWPReadDataHeaderSlots: Invalid file version (%d)",
3114 phrec.version);
3115 errno = ENOSYS;
3116 return False;
3117 }
3118 else if(phrec.version == 2){
3119 slot_off = 132; /* see above layout of bytes */
3120 hdr_off = phrec.hdr_len;
3121 }
3122 else{
3123 slot_off = 152; /* see above layout of bytes */
3124
3125 /*
3126 * Find offset to the end of the "header".
3127 */
3128 hdr_off = MIN(phrec.oset_skiprecs,phrec.oset_datarecs);
3129 if(!hdr_off){
3130 hdr_off = MAX(phrec.oset_skiprecs,phrec.oset_datarecs);
3131 }
3132 }
3133
3134 /*
3135 * validate nslots passed in with what is in the file.
3136 * hdr_off should point to the offset in the file where the slots
3137 * are finished and the 1 block of zero padding is finished.
3138 */
3139 fileslots = hdr_off - slot_off; /* bytes for slots */
3140
3141 /*
3142 * bytes for slots/zero padding must be of block size 16
3143 */
3144 if(fileslots%16){
3145 OWPError(ctx,OWPErrFATAL,EFTYPE,
3146 "OWPReadDataHeaderSlots: Invalid hdr_offset (%" PRIu64 ")",
3147 hdr_off);
3148 /*
3149 * TODO: Check validity of this errno... May need to
3150 * use ENOSYS...
3151 */
3152 errno = EFTYPE;
3153 return False;
3154 }
3155
3156 /*
3157 * Convert bytes to number of slots. Divide by block size, then
3158 * subtract 1 for zero integrity block.
3159 */
3160 fileslots/=16;
3161 fileslots--;
3162
3163 if(fileslots != nslots){
3164 OWPError(ctx,OWPErrFATAL,EINVAL,
3165 "OWPReadDataHeaderSlots: nslots mismatch with file: fileslots(%d), nslots(%d)",
3166 fileslots,nslots);
3167 errno = EINVAL;
3168 return False;
3169 }
3170
3171 /*
3172 * Position fp to beginning of slot records.
3173 */
3174 if(fseeko(fp,slot_off,SEEK_SET)){
3175 err = errno;
3176 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
3177 errno = err;
3178 return False;
3179 }
3180
3181 for(i=0;i<nslots;i++){
3182
3183 /*
3184 * Read slot into buffer.
3185 */
3186 if(fread(msg,1,16,fp) != 16){
3187 err = errno;
3188 OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
3189 errno = err;
3190 return False;
3191 }
3192
3193 /*
3194 * Decode slot buffer into slot record.
3195 */
3196 if(_OWPDecodeSlot(&slots[i],msg) != OWPErrOK){
3197 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3198 "OWPReadDataHeaderSlots: Invalid Slot record");
3199 errno = EFTYPE;
3200 return False;
3201 }
3202 }
3203
3204 /*
3205 * Read block of Zero Integrity bytes into buffer.
3206 */
3207 if(fread(msg,1,16,fp) != 16){
3208 err = errno;
3209 OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
3210 errno = err;
3211 return False;
3212 }
3213
3214 /*
3215 * check to make sure Zero bytes are zero.
3216 */
3217 memset(zero,0,16);
3218 if(memcmp(zero,msg,16) != 0){
3219 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3220 "OWPReadDataHeaderSlots: Invalid zero padding");
3221 errno = EFTYPE;
3222 return False;
3223 }
3224
3225 return True;
3226 }
3227
3228 /*
3229 * Function: OWPParseRecords
3230 *
3231 * Description:
3232 * Fetch num_rec records from disk calling the record proc function
3233 * on each record.
3234 *
3235 * In Args:
3236 *
3237 * Out Args:
3238 *
3239 * Scope:
3240 * Returns:
3241 * Side Effect:
3242 */
3243 OWPErrSeverity
OWPParseRecords(OWPContext ctx,FILE * fp,uint32_t num_rec,uint32_t file_version,OWPDoDataRecord proc_rec,void * app_data)3244 OWPParseRecords(
3245 OWPContext ctx,
3246 FILE *fp,
3247 uint32_t num_rec,
3248 uint32_t file_version,
3249 OWPDoDataRecord proc_rec,
3250 void *app_data
3251 )
3252 {
3253 size_t len_rec;
3254 char rbuf[_OWP_MAXDATAREC_SIZE];
3255 uint32_t i;
3256 OWPDataRec rec;
3257 int rc;
3258
3259 /*
3260 * This function is used to abstract away the different requirements
3261 * of different versions of the owd data files.
3262 * Currently it supports 0 and 2, (both of which
3263 * require the same 24 octet data records) and 3 which requires
3264 * 25 octets.
3265 */
3266 switch(file_version){
3267 case 0: case 2:
3268 len_rec = _OWP_DATARECV2_SIZE;
3269 break;
3270 case 3:
3271 len_rec = _OWP_DATAREC_SIZE;
3272 break;
3273 default:
3274 OWPError(ctx,OWPErrFATAL,EINVAL,
3275 "OWPParseRecords: Invalid file version (%d)",
3276 file_version);
3277 return OWPErrFATAL;
3278 }
3279
3280 for(i=0;i<num_rec;i++){
3281 if(fread(rbuf,len_rec,1,fp) < 1){
3282 if(ferror(fp)){
3283 OWPError(ctx,OWPErrFATAL,errno,
3284 "fread(): STREAM ERROR: offset=%" PRIuPTR ",i=%" PRIu32,
3285 ftello(fp),i);
3286 }
3287 else if(feof(fp)){
3288 OWPError(ctx,OWPErrFATAL,errno,
3289 "fread(): EOF: offset=%" PRIu64,ftello(fp));
3290 }
3291 return OWPErrFATAL;
3292 }
3293 if(!_OWPDecodeDataRecord(file_version,&rec,rbuf)){
3294 errno = EFTYPE;
3295 OWPError(ctx,OWPErrFATAL,errno,
3296 "OWPParseRecords: Invalid Data Record: %M");
3297 return OWPErrFATAL;
3298 }
3299 rc = proc_rec(&rec,app_data);
3300 if(!rc) continue;
3301 if(rc < 0)
3302 return OWPErrFATAL;
3303 return OWPErrOK;
3304
3305 }
3306
3307 return OWPErrOK;
3308 }
3309
3310 /*
3311 * Function: OWPReadDataSkips
3312 *
3313 * Description:
3314 * This function will read all the skip records out of the
3315 * file fp. skips is assumed to be an array of OWPSkip records of
3316 * length nskips.
3317 *
3318 * In Args:
3319 *
3320 * Out Args:
3321 *
3322 * Scope:
3323 * Returns:
3324 * Side Effect:
3325 */
3326 OWPBoolean
OWPReadDataSkips(OWPContext ctx,FILE * fp,uint32_t nskips,OWPSkip skips)3327 OWPReadDataSkips(
3328 OWPContext ctx,
3329 FILE *fp,
3330 uint32_t nskips,
3331 OWPSkip skips
3332 )
3333 {
3334 int err;
3335 _OWPSessionHeaderInitialRec phrec;
3336 uint32_t i;
3337
3338 /* buffer for Skips 32 bit aligned */
3339 char msg[_OWP_SKIPREC_SIZE];
3340
3341 /*
3342 * validate array.
3343 */
3344 assert(skips);
3345
3346 /*
3347 * Stat the file and get the "initial" fields from the header.
3348 */
3349 if(!_OWPReadDataHeaderInitial(ctx,fp,&phrec)){
3350 return False;
3351 }
3352
3353 /*
3354 * this function is currently only supported for version 2 files.
3355 */
3356 if(phrec.version < 2){
3357 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
3358 "OWPReadDataSkips: Invalid file version (%d)",
3359 phrec.version);
3360 errno = ENOSYS;
3361 return False;
3362 }
3363
3364 if(phrec.num_skiprecs != nskips){
3365 OWPError(ctx,OWPErrFATAL, OWPErrINVALID,
3366 "OWPReadDataSkips: nskips requested (%lu) doesn't match file (%lu).",
3367 nskips,phrec.num_skiprecs);
3368
3369 return False;
3370 }
3371
3372 /*
3373 * Position fp to beginning of skip records.
3374 */
3375 if(fseeko(fp,phrec.oset_skiprecs,SEEK_SET)){
3376 err = errno;
3377 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fseeko(): %M");
3378 errno = err;
3379 return False;
3380 }
3381
3382 for(i=0;i<nskips;i++){
3383
3384 /*
3385 * Read slot into buffer.
3386 */
3387 if(fread(msg,1,_OWP_SKIPREC_SIZE,fp) != _OWP_SKIPREC_SIZE){
3388 err = errno;
3389 OWPError(ctx,OWPErrFATAL,errno,"fread(): %M");
3390 errno = err;
3391 return False;
3392 }
3393
3394 /*
3395 * Decode slot buffer into slot record.
3396 */
3397 _OWPDecodeSkipRecord(&skips[i],msg);
3398 }
3399
3400 return True;
3401 }
3402
3403 /*
3404 * Function: OWPTestDiskspace
3405 *
3406 * Description:
3407 * Returns the size of file a given testspec will require.
3408 * (Specific to version 3 files - all write functions only
3409 * support latest version of files.)
3410 *
3411 * In Args:
3412 *
3413 * Out Args:
3414 *
3415 * Scope:
3416 * Returns:
3417 * Side Effect:
3418 */
3419 uint64_t
OWPTestDiskspace(OWPTestSpec * tspec)3420 OWPTestDiskspace(
3421 OWPTestSpec *tspec
3422 )
3423 {
3424 uint64_t hdr_len;
3425
3426 /*
3427 * 56 == 40 for initial portion + 16 for ending IZP
3428 */
3429 hdr_len = 56 + +_OWP_TEST_REQUEST_PREAMBLE_SIZE+
3430 16*(tspec->nslots+1);
3431 return hdr_len + tspec->npackets*_OWP_DATAREC_SIZE;
3432 }
3433
3434 /*
3435 * Function: OWPIsLostRecord
3436 *
3437 * Description:
3438 * Returns true if the given DataRec indicates a "lost" packet. This
3439 * is determined by looking at the recv timestamp. If it is a string
3440 * of zero bits, then it is lost.
3441 *
3442 * In Args:
3443 *
3444 * Out Args:
3445 *
3446 * Scope:
3447 * Returns:
3448 * Side Effect:
3449 */
3450 OWPBoolean
OWPIsLostRecord(OWPDataRec * rec)3451 OWPIsLostRecord(
3452 OWPDataRec *rec
3453 )
3454 {
3455 return !rec->recv.owptime;
3456 }
3457