1 /*
2 * $Id: endpoint.c 1072 2009-10-21 19:08:02Z aaron $
3 */
4 /************************************************************************
5 * *
6 * Copyright (C) 2002 *
7 * Internet2 *
8 * All Rights Reserved *
9 * *
10 ************************************************************************/
11 /*
12 * File: endpoint.c
13 *
14 * Author: Jeff W. Boote
15 * Internet2
16 *
17 * Date: Wed May 29 09:17:21 MDT 2002
18 *
19 * Description:
20 * This file contains the "default" implementation for
21 * the send and recv endpoints of an OWAMP test session.
22 */
23 #include "owampP.h"
24
25 #include <stdio.h>
26 #include <math.h>
27 #include <sys/types.h>
28 #include <sys/mman.h>
29 #include <sys/uio.h>
30 #include <unistd.h>
31 #include <signal.h>
32 #include <netinet/in.h>
33 #include <assert.h>
34 #include <sys/socket.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37
38 /*
39 * Some systems (Solaris ahem...) don't define the CMSG_SPACE macro.
40 * It does define related macros - I will attempt to do the "right thing".
41 */
42 #ifndef CMSG_SPACE
43 #if defined(_CMSG_DATA_ALIGN) && defined(_CMSG_HDR_ALIGN)
44 #define CMSG_SPACE(len) \
45 (_CMSG_DATA_ALIGN(len) + _CMSG_DATA_ALIGN(sizeof(struct cmsghdr)))
46 #else
47 #error "CMSG_SPACE macro undefined for this OS - work to do..."
48 #endif
49 #endif
50
51 /*
52 * Function: EndpointAlloc
53 *
54 * Description:
55 * Allocate a record to keep track of the state information for
56 * this endpoint. (Much of this state is also in the control record
57 * and the TestSession record... May simplify this in the future
58 * to just reference the other records.)
59 *
60 * In Args:
61 *
62 * Out Args:
63 *
64 * Scope:
65 * Returns:
66 * Side Effect:
67 */
68 static OWPEndpoint
EndpointAlloc(OWPControl cntrl)69 EndpointAlloc(
70 OWPControl cntrl
71 )
72 {
73 OWPEndpoint ep = calloc(1,sizeof(OWPEndpointRec));
74
75 if(!ep){
76 OWPError(cntrl->ctx,OWPErrFATAL,errno,"malloc(EndpointRec)");
77 return NULL;
78 }
79
80 ep->cntrl = cntrl;
81 ep->sockfd = -1;
82 ep->skiprecfd = -1;
83 ep->acceptval = OWP_CNTRL_INVALID;
84 ep->wopts = WNOHANG;
85
86 return ep;
87 }
88
89 static void
LostFree(OWPLostPacket lost)90 LostFree(
91 OWPLostPacket lost
92 )
93 {
94 OWPLostPacket lt;
95
96 while(lost){
97 lt = lost->next;
98 free(lost);
99 lost = lt;
100 }
101
102 return;
103 }
104
105 static void
SkipFree(_OWPSkip skip)106 SkipFree(
107 _OWPSkip skip
108 )
109 {
110 _OWPSkip st;
111
112 while(skip){
113 st = skip->next;
114 free(skip);
115 skip=st;
116 }
117
118 return;
119 }
120
121 /*
122 * Function: EndpointClear
123 *
124 * Description:
125 * Clear out any resources that are used in the Endpoint record
126 * that are not needed in the parent process after the endpoint
127 * forks off to do the actual test.
128 *
129 * In Args:
130 *
131 * Out Args:
132 *
133 * Scope:
134 * Returns:
135 * Side Effect:
136 */
137 static void
EndpointClear(OWPEndpoint ep)138 EndpointClear(
139 OWPEndpoint ep
140 )
141 {
142 if(!ep)
143 return;
144
145 if(ep->sockfd > -1){
146 close(ep->sockfd);
147 ep->sockfd = -1;
148 }
149
150 if(ep->payload){
151 free(ep->payload);
152 ep->payload = NULL;
153 }
154
155 if(ep->hmac_ctx){
156 I2HMACSha1Free(ep->hmac_ctx);
157 ep->hmac_ctx = NULL;
158 }
159
160 if(ep->lost_packet_buffer){
161 I2HashClose(ep->lost_packet_buffer);
162 }
163 ep->lost_packet_buffer = NULL;
164 LostFree(ep->lost_allocated);
165 ep->lost_allocated = NULL;
166 SkipFree(ep->skip_allocated);
167 ep->skip_allocated = NULL;
168
169 return;
170 }
171
172 /*
173 * Function: EndpointFree
174 *
175 * Description:
176 * completely free all resoruces associated with an endpoint record.
177 *
178 * In Args:
179 *
180 * Out Args:
181 *
182 * Scope:
183 * Returns:
184 * Side Effect:
185 */
186 static void
EndpointFree(OWPEndpoint ep,OWPAcceptType aval)187 EndpointFree(
188 OWPEndpoint ep,
189 OWPAcceptType aval
190 )
191 {
192 if(!ep)
193 return;
194
195 EndpointClear(ep);
196
197 if(ep->skiprecfd > -1){
198 close(ep->skiprecfd);
199 ep->skiprecfd = -1;
200 }
201 if(ep->datafile){
202 fflush(ep->datafile);
203 fsync(fileno(ep->datafile));
204 fclose(ep->datafile);
205 ep->datafile = NULL;
206 }
207 if(ep->fbuff){
208 free(ep->fbuff);
209 ep->fbuff = NULL;
210 }
211
212 if(ep->userfile){
213 fflush(ep->userfile);
214 fsync(fileno(ep->userfile));
215 _OWPCallCloseFile(ep->cntrl,ep->tsession->closure,ep->userfile,
216 aval);
217 ep->userfile = NULL;
218 }
219
220 free(ep);
221
222 return;
223 }
224
225 /*
226 * Function: reopen_datafile
227 *
228 * Description:
229 * This function takes a fp and creates a new fp to the same file
230 * record. This is used to ensure that the fp used for the actual
231 * test is buffered properly. And - allows the test to write to the
232 * same file without modifying a fp passed in by an application.
233 *
234 * In Args:
235 *
236 * Out Args:
237 *
238 * Scope:
239 * Returns:
240 * Side Effect:
241 */
242 static FILE*
reopen_datafile(OWPContext ctx,FILE * infp)243 reopen_datafile(
244 OWPContext ctx,
245 FILE *infp
246 )
247 {
248 int newfd;
249 FILE *fp;
250
251 if( (newfd = dup(fileno(infp))) < 0){
252 OWPError(ctx,OWPErrFATAL,errno,"dup(%d): %M",
253 fileno(infp));
254 return NULL;
255 }
256
257 if( !(fp = fdopen(newfd,"r+b"))){
258 OWPError(ctx,OWPErrFATAL,errno, "fdopen(%d): %M",newfd);
259 return NULL;
260 }
261
262 return fp;
263 }
264
265 /*
266 * Function: CmpLostPacket
267 *
268 * Description:
269 * Used to compare the 32 bit keys for the OWPLostPacket records.
270 *
271 * In Args:
272 *
273 * Out Args:
274 *
275 * Scope:
276 * Returns:
277 * Side Effect:
278 */
279 static int
CmpLostPacket(I2Datum x,I2Datum y)280 CmpLostPacket(
281 I2Datum x,
282 I2Datum y
283 )
284 {
285 uint32_t *xn = (uint32_t*)x.dptr;
286 uint32_t *yn = (uint32_t*)y.dptr;
287
288 return !(*xn == *yn);
289 }
290
291 /*
292 * Function: HashLostPacket
293 *
294 * Description:
295 *
296 * In Args:
297 *
298 * Out Args:
299 *
300 * Scope:
301 * Returns:
302 * Side Effect:
303 */
304 uint32_t
HashLostPacket(I2Datum k)305 HashLostPacket(
306 I2Datum k
307 )
308 {
309 uint32_t *kn = (uint32_t*)k.dptr;
310
311 return *kn & 0xFFFFUL;
312 }
313
314 static int
anon_file(OWPContext ctx)315 anon_file(
316 OWPContext ctx
317 )
318 {
319 char *tmpdir = NULL;
320 char *fpath = NULL;
321 int pathlen;
322 int fd = -1;
323
324 if( !(tmpdir = getenv("TMPDIR"))){
325 tmpdir = _OWP_DEFAULT_TMPDIR;
326 }
327
328 pathlen = strlen(tmpdir) + strlen(OWP_PATH_SEPARATOR) +
329 strlen(_OWP_SKIPFILE_FMT) + 1;
330
331 if(pathlen > PATH_MAX){
332 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"TMPDIR too long");
333 goto error;
334 }
335
336 if( !(fpath = calloc((size_t)pathlen,sizeof(char)))){
337 OWPError(ctx,OWPErrFATAL,errno,"calloc(%d): %M",pathlen);
338 goto error;
339 }
340
341 if(snprintf(fpath,pathlen,"%s%s%s",tmpdir,OWP_PATH_SEPARATOR,
342 _OWP_SKIPFILE_FMT) != (pathlen-1)){
343 OWPError(ctx,OWPErrFATAL,errno,"snprintf(): Wrong len");
344 goto error;
345 }
346
347 if( (fd = mkstemp(fpath)) < 0){
348 OWPError(ctx,OWPErrFATAL,errno,"mkstemp(%s): Check directory permissions: %M",fpath);
349 goto error;
350 }
351
352 if(unlink(fpath) != 0){
353 OWPError(ctx,OWPErrFATAL,errno,"unlink(): %M");
354 goto error;
355 }
356
357 free(fpath);
358 return fd;
359
360 error:
361 if(fpath) free(fpath);
362 if(fd > -1) close(fd);
363 return -1;
364 }
365
366
367 /*
368 * The endpoint init function is responsible for opening a socket, and
369 * allocating a local port number. (And attempting to allocate all recv
370 * side memory/file resources that are required so failure is not as likely
371 * during an actual test.)
372 */
373 OWPBoolean
_OWPEndpointInit(OWPControl cntrl,OWPTestSession tsession,I2Addr localaddr,FILE * fp,OWPAcceptType * aval,OWPErrSeverity * err_ret)374 _OWPEndpointInit(
375 OWPControl cntrl,
376 OWPTestSession tsession,
377 I2Addr localaddr,
378 FILE *fp,
379 OWPAcceptType *aval,
380 OWPErrSeverity *err_ret
381 )
382 {
383 struct sockaddr_storage sbuff;
384 socklen_t sbuff_len=sizeof(sbuff);
385 struct sockaddr *saddr;
386 socklen_t saddrlen;
387 OWPEndpoint ep;
388 OWPPacketSizeT tpsize;
389 int sbuf_size;
390 int sopt;
391 socklen_t opt_size;
392 uint32_t i;
393 OWPTimeStamp tstamp;
394 uint16_t port=0;
395 uint16_t p;
396 uint16_t range;
397 OWPPortRange portrange=NULL;
398 int saveerr=0;
399 char localnode[NI_MAXHOST];
400 size_t localnodelen = sizeof(localnode);
401 double enddelay = _OWP_DEFAULT_FUZZTIME;
402 double *enddelayptr;
403
404 *err_ret = OWPErrFATAL;
405 *aval = OWP_CNTRL_UNAVAILABLE_TEMP;
406
407 if( !I2AddrNodeName(localaddr,localnode,&localnodelen)){
408 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
409 "I2AddrNodeName(): failed for localaddr");
410 return False;
411 }
412
413 if( !(ep=EndpointAlloc(cntrl)))
414 return False;
415
416 ep->send = (localaddr == tsession->sender);
417
418 ep->tsession = tsession;
419 ep->cntrl = cntrl;
420
421 if( !(saddr = I2AddrSAddr(localaddr,&saddrlen))){
422 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
423 "_EndpointInit: Unable to get saddr information");
424 goto error;
425 }
426
427 tpsize = OWPTestPacketSize(saddr->sa_family,
428 ep->cntrl->mode,tsession->test_spec.packet_size_padding);
429 tpsize += 128; /* Add fuzz space for IP "options" */
430 sbuf_size = tpsize;
431 if((OWPPacketSizeT)sbuf_size != tpsize){
432 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
433 "Packet size overflow - invalid padding");
434 *aval = OWP_CNTRL_FAILURE;
435 goto error;
436 }
437
438 ep->len_payload = OWPTestPayloadSize(ep->cntrl->mode,
439 ep->tsession->test_spec.packet_size_padding);
440 /* use calloc to initialize the memory to 0 */
441 ep->payload = calloc(1,ep->len_payload);
442
443 if(!ep->payload){
444 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,"calloc(): %M");
445 goto error;
446 }
447
448 tstamp.owptime = ep->tsession->test_spec.start_time;
449 (void)OWPTimestampToTimespec(&ep->start,&tstamp);
450
451 /*
452 * Create the socket.
453 */
454 ep->sockfd = socket(saddr->sa_family,I2AddrSocktype(localaddr),
455 I2AddrProtocol(localaddr));
456 if(ep->sockfd<0){
457 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,"socket(): %M");
458 goto error;
459 }
460
461 /*
462 * Determine what port to try:
463 */
464 /* type punning */
465
466 /* first - see if saddr specifies a port directly... */
467 switch(saddr->sa_family){
468 struct sockaddr_in *s4;
469 #ifdef AF_INET6
470 struct sockaddr_in6 *s6;
471
472 case AF_INET6:
473 s6 = (struct sockaddr_in6*)saddr;
474 port = ntohs(s6->sin6_port);
475 break;
476 #endif
477 case AF_INET:
478 s4 = (struct sockaddr_in*)saddr;
479 port = ntohs(s4->sin_port);
480 break;
481 default:
482 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
483 "Invalid address family for test");
484 *aval = OWP_CNTRL_UNSUPPORTED;
485 goto error;
486 }
487
488
489 if(port){
490 /*
491 * port specified by saddr
492 */
493 p = port;
494 }
495 else if(!(portrange = (OWPPortRange)OWPContextConfigGetV(cntrl->ctx,
496 OWPTestPortRange))){
497 p = port = 0;
498 }else{
499 uint32_t r;
500
501 /*
502 * Get a random 32 bit number to aid in selecting first
503 * port to try.
504 */
505 if(I2RandomBytes(cntrl->ctx->rand_src,(uint8_t*)&r,4) != 0)
506 goto error;
507
508 if(portrange->high < portrange->low){
509 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
510 "Invalid port range specified");
511 *aval = OWP_CNTRL_FAILURE;
512 goto error;
513 }
514
515 range = portrange->high - portrange->low + 1;
516 p = port = portrange->low + ((double)r / 0xffffffff * range);
517 }
518
519 do{
520 /* Specify the port number */
521 switch(saddr->sa_family){
522 struct sockaddr_in *s4;
523 #ifdef AF_INET6
524 struct sockaddr_in6 *s6;
525
526 case AF_INET6:
527 s6 = (struct sockaddr_in6*)saddr;
528 s6->sin6_port = htons(p);
529 break;
530 #endif
531 case AF_INET:
532 s4 = (struct sockaddr_in*)saddr;
533 s4->sin_port = htons(p);
534 break;
535 default:
536 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
537 "Invalid address family for test");
538 *aval = OWP_CNTRL_UNSUPPORTED;
539 goto error;
540 }
541
542 /*
543 * Try binding.
544 */
545 if(bind(ep->sockfd,saddr,saddrlen) == 0)
546 goto success;
547 /*
548 * If it failed, and we are not using a "range" then exit
549 * loop and report failure. (Or if the error is not EADDRINUSE
550 * this is a permenent failure.)
551 */
552 if(!portrange || !range || (errno != EADDRINUSE)){
553 *aval = OWP_CNTRL_FAILURE;
554 goto bind_fail;
555 }
556
557 /*
558 * compute next port to try.
559 */
560 if(range){
561 p -= portrange->low;
562 p = (p + 1) % range;
563 p += portrange->low;
564 }
565 } while(p != port);
566
567 saveerr = errno;
568 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
569 "Full port range exhausted");
570 bind_fail:
571 if(!saveerr) saveerr = errno;
572 OWPError(cntrl->ctx,OWPErrFATAL,saveerr,"bind([%s]:%d): %M",localnode,p);
573 goto error;
574
575 success:
576
577 /*
578 * Retrieve the saddr as defined by the system.
579 */
580 memset(&sbuff,0,sizeof(sbuff));
581 if(getsockname(ep->sockfd,(void*)&sbuff,&sbuff_len) != 0){
582 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
583 "getsockname(): %M");
584 goto error;
585 }
586
587 /*
588 * set saddr to the sockaddr that was actually used.
589 * (This sets the port in saddr as well.)
590 */
591 assert(saddrlen >= sbuff_len);
592 memcpy(saddr,&sbuff,sbuff_len);
593
594 /*
595 * Reset the saddr into the I2Addr so it reflects the new
596 * port number.
597 */
598 if( !I2AddrSetSAddr(localaddr,saddr,saddrlen)){
599 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
600 "I2AddrSetSAddr(): Resetting saddr");
601 goto error;
602 }
603
604 /*
605 * If we are receiver, sid is valid and we need to open file.
606 */
607 if(!ep->send){
608 size_t size;
609 OWPLostPacket alist;
610
611 /*
612 * pre-allocate nodes for lost_packet buffer.
613 * (estimate number of nodes needed to hold enough
614 * packets for 2*Loss-timeout)
615 * TODO: determine a reasonable number instead of (2).
616 * (2 is just a guess... exp distribution probably
617 * converges to 0 fast enough that we could get away
618 * with a much smaller number... say 1.2)
619 *
620 * It is possible that the actual distribution will make
621 * it necessary to hold more than this many nodes in the
622 * buffer - but it is highly unlikely. If that happens,
623 * another dynamic allocation will happen. This should
624 * at least minimize the dynamic allocations during the
625 * test.
626 */
627 #define PACKBUFFALLOCFACTOR 2
628
629 ep->freelist=NULL;
630 ep->numalist = OWPTestPacketRate(cntrl->ctx,
631 &tsession->test_spec) *
632 OWPNum64ToDouble(
633 tsession->test_spec.loss_timeout) *
634 PACKBUFFALLOCFACTOR;
635 ep->numalist = MAX(ep->numalist,100);
636
637 if(!(alist = calloc(ep->numalist,sizeof(OWPLostPacketRec)))){
638 OWPError(cntrl->ctx,OWPErrFATAL,errno,"calloc(): %M");
639 goto error;
640 }
641
642 /*
643 * [0] is used to track the list of allocated arrays so they
644 * can be freed.
645 */
646 ep->lost_allocated = alist;
647 for(i=1;i<ep->numalist;i++){
648 alist[i].next = ep->freelist;
649 ep->freelist = &alist[i];
650 }
651
652 if(!(ep->lost_packet_buffer = I2HashInit(cntrl->ctx->eh,ep->numalist,
653 CmpLostPacket,HashLostPacket))){
654 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
655 "_OWPEndpointInit: Unable to initialize lost packet buffer");
656 goto error;
657 }
658
659 ep->fname[0] = '\0';
660 if(!fp){
661 ep->userfile = fp = _OWPCallOpenFile(cntrl,
662 tsession->closure,
663 tsession->sid,
664 ep->fname);
665 }
666
667 if(!fp){
668 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
669 "Unable to open session file(%s): %M",
670 ep->fname);
671 goto error;
672 }
673
674 /*
675 * This function dup's the fd/fp so that file buffering
676 * can be reset.
677 */
678 if( !(ep->datafile = reopen_datafile(cntrl->ctx,fp))){
679 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
680 "Unable to re-open session file(%s): %M",
681 ep->fname);
682 goto error;
683 }
684
685 /*
686 * Determine "optimal" file buffer size. To allow "Fetch"
687 * clients to access ongoing tests - we define "optimal" as
688 * approximately 1 second of buffering.
689 */
690
691 /*
692 * Determine data rate. i.e. size/second.
693 */
694 size = OWPTestPacketRate(cntrl->ctx,&ep->tsession->test_spec) *
695 _OWP_DATAREC_SIZE;
696
697 if(size < _OWP_DATAREC_SIZE){
698 /* If rate is less than one packet/second then unbuffered */
699 setvbuf(ep->datafile,NULL,_IONBF,0);
700 }
701 else{
702 struct stat statbuf;
703
704 /* stat to find out st_blksize */
705 if(fstat(fileno(ep->datafile),&statbuf) != 0){
706 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
707 "fstat(): %M");
708 goto error;
709 }
710
711 /*
712 * Don't make buffer larger than "default"
713 */
714 size = MIN(size,(size_t)statbuf.st_blksize);
715
716
717 if( !(ep->fbuff = malloc(size))){
718 OWPError(cntrl->ctx,OWPErrFATAL,errno,"malloc(): %M");
719 goto error;
720 }
721 setvbuf(ep->datafile,ep->fbuff,_IOFBF,size);
722 }
723
724 /*
725 * receiver - need to set the recv buffer size large
726 * enough for the packet, so we can get it in a single
727 * recv.
728 */
729 opt_size = sizeof(sopt);
730 if(getsockopt(ep->sockfd,SOL_SOCKET,SO_RCVBUF,
731 (void*)&sopt,&opt_size) < 0){
732 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
733 "getsockopt(RCVBUF): %M");
734 goto error;
735 }
736
737 if(sopt < sbuf_size){
738 sopt = sbuf_size;
739 if(setsockopt(ep->sockfd,SOL_SOCKET,SO_RCVBUF,
740 (void*)&sopt,sizeof(sopt)) < 0){
741 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
742 "setsockopt(RCVBUF=%d): %M",sopt);
743 goto error;
744 }
745 }
746
747 /*
748 * Request TTL information in ancillary data.
749 * TODO: Determine correct sockopt for IPV6!
750 */
751 switch(saddr->sa_family){
752 #ifdef AF_INET6
753 case AF_INET6:
754 #ifdef IPV6_RECVHOPLIMIT
755 sopt = 1;
756 if(setsockopt(ep->sockfd,IPPROTO_IPV6,
757 IPV6_RECVHOPLIMIT,
758 (void*)&sopt,sizeof(sopt)) < 0){
759 OWPError(cntrl->ctx,OWPErrFATAL,
760 OWPErrUNKNOWN,
761 "setsockopt(IPV6_RECVHOPLIMIT=1): %M");
762 goto error;
763 }
764 #endif
765 break;
766 #endif
767 case AF_INET:
768 #ifdef IP_RECVTTL
769 sopt = 1;
770 if(setsockopt(ep->sockfd,IPPROTO_IP,IP_RECVTTL,
771 (void*)&sopt,sizeof(sopt)) < 0){
772 OWPError(cntrl->ctx,OWPErrFATAL,
773 OWPErrUNKNOWN,
774 "setsockopt(IP_RECVTTL=1): %M");
775 goto error;
776 }
777 #endif
778 break;
779 default:
780 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
781 "Invalid address family for test");
782 *aval = OWP_CNTRL_UNSUPPORTED;
783 goto error;
784 }
785 }
786 else{
787 _OWPSkip askip;
788
789 /*
790 * Create a file for sharing skip records. Shared memory could
791 * work for this, but porting is very painful and performance is
792 * not so important for this step. (yet)
793 *
794 * The child process will fill this with Skip information that
795 * the parent will read after the child exits.
796 *
797 * Note that this could not be done with a socket/pipe because it
798 * is unknown how much data will be coming through, and the parent
799 * api gives control of the "event loop" back to the application.
800 * Therefore, there is no easy way of adding a "select" for the
801 * new fd. It is possible the child will be sending more data
802 * than a pipe implementation would buffer, therefore the child
803 * process would need to stay around until the pipe is completely
804 * read. Using a file/shm implementation allows the data to be around
805 * after the child exits no matter the size.
806 */
807
808 if( (ep->skiprecfd = anon_file(cntrl->ctx)) < 0){
809 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
810 "Unable to create skips file");
811 goto error;
812 }
813
814 /*
815 * pre-allocate nodes for skipped packet buffer.
816 *
817 * Will initially allocate MAX(100,(.10*npackets)).
818 * The worst case is .5*npackets (if every other
819 * packet needed to be skipped) but in most cases
820 * this list of holes will be much smaller. This
821 * list will dynamically grow if needed. This is
822 * being pre-allocated to at least minimize the number
823 * of dynamic allocations tht need to happen during
824 * a test.
825 */
826 #define PACKBUFFALLOCFACTOR 2
827
828 ep->free_skiplist=NULL;
829 ep->num_allocskip = .10 * ep->tsession->test_spec.npackets;
830 ep->num_allocskip = MAX(ep->num_allocskip,100);
831
832 if(!(askip = calloc(ep->num_allocskip,sizeof(_OWPSkipRec)))){
833 OWPError(cntrl->ctx,OWPErrFATAL,errno,"calloc(): %M");
834 goto error;
835 }
836
837 /*
838 * [0] is used to track the list of allocated arrays so they
839 * can be freed.
840 */
841 ep->skip_allocated = askip;
842 for(i=1;i<ep->num_allocskip;i++){
843 askip[i].next = ep->free_skiplist;
844 ep->free_skiplist = &askip[i];
845 }
846
847 /*
848 * Sender needs to set sockopt's to ensure test
849 * packets don't fragment in the socket api.
850 */
851
852 opt_size = sizeof(sopt);
853 if(getsockopt(ep->sockfd,SOL_SOCKET,SO_SNDBUF,
854 (void*)&sopt,&opt_size) < 0){
855 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
856 "getsockopt(SNDBUF): %M");
857 goto error;
858 }
859
860 if(sopt < sbuf_size){
861 sopt = sbuf_size;
862 if(setsockopt(ep->sockfd,SOL_SOCKET,SO_SNDBUF,
863 (void*)&sopt,sizeof(sopt)) < 0){
864 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
865 "setsockopt(RCVBUF=%d): %M",
866 sopt);
867 goto error;
868 }
869 }
870
871 /*
872 * draft-ietf-ippm-owdp-08.txt adds TTL to the data that
873 * is stored by the receiver. The sender should set TTL
874 * to 255 to make this useful. (hoplimit is the field
875 * name in IPv6.)
876 */
877 switch(saddr->sa_family){
878 #ifdef AF_INET6
879 case AF_INET6:
880 #ifdef IPV6_UNICAST_HOPS
881 sopt = 255;
882 if(setsockopt(ep->sockfd,IPPROTO_IPV6,
883 IPV6_UNICAST_HOPS,
884 (void*)&sopt,sizeof(sopt)) < 0){
885 OWPError(cntrl->ctx,OWPErrFATAL,
886 OWPErrUNKNOWN,
887 "setsockopt(IPV6_UNICAST_HOPS=%d): %M",
888 sopt);
889 goto error;
890 }
891 #endif
892 break;
893 #endif
894 case AF_INET:
895 #ifdef IP_TTL
896 sopt = 255;
897 if(setsockopt(ep->sockfd,IPPROTO_IP,IP_TTL,
898 (void*)&sopt,sizeof(sopt)) < 0){
899 OWPError(cntrl->ctx,OWPErrFATAL,
900 OWPErrUNKNOWN,
901 "setsockopt(IP_TTL=%d): %M",
902 sopt);
903 goto error;
904 }
905 #endif
906 break;
907 default:
908 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
909 "Invalid address family for test");
910 *aval = OWP_CNTRL_UNSUPPORTED;
911 goto error;
912 }
913
914 if(ep->tsession->test_spec.typeP){
915 int optname = IP_TOS;
916 int optlevel = IP_TOS;
917
918 /*
919 * TODO: Decoding of typeP will need to change if
920 * the code can ever support PHB directly(RFC 2836). (Need
921 * support in the socket API to do this... Not sure it really
922 * makes sense - DSCP values really map to these at the
923 * router... Perhaps the owamp spec should not have 16 bits for
924 * this.) In any case, if this is ever to happen directly in
925 * owamp, this code will need to look at first two bits and do
926 * something different (copy more than
927 * the next 6 bits).
928 *
929 * For now, just verify typeP set to valid value
930 * for DSCP mode:
931 * Only 6 bits can be set for it to be valid
932 * (bits 2-7 of the high-order byte)
933 */
934 if(ep->tsession->test_spec.typeP & ~0x3F000000){
935 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNSUPPORTED,
936 "Unsupported TypeP requested");
937 /*
938 * Set err_ret to OK - this was a valid
939 * request, this implementation just doesn't
940 * support it.
941 */
942 *aval = OWP_CNTRL_UNSUPPORTED;
943 *err_ret = OWPErrOK;
944 goto error;
945 }
946 /*
947 * TODO: When I find a kernel that actually has IPV6_TCLASS
948 * make sure it works. (This looks like the RFC 3542 way...)
949 */
950 switch(saddr->sa_family){
951 case AF_INET:
952 optlevel = IPPROTO_IP;
953 optname = IP_TOS;
954 break;
955 #ifdef AF_INET6
956 case AF_INET6:
957 optlevel = IPPROTO_IPV6;
958 /*
959 * Look for RFC 3542 sockopts - have no systems with them, but look
960 * for them anyway...
961 */
962 #ifdef IPV6_TCLASS
963 optname = IPV6_TCLASS;
964 #else
965 optname = IP_TOS;
966 #endif
967 break;
968 #endif
969 default:
970 /*NOTREACHED*/
971 break;
972 }
973
974 /* Copy high-order byte (minus first two bits) */
975 sopt = (uint8_t)(ep->tsession->test_spec.typeP >> 24);
976 sopt &= 0x3F; /* this should be a no-op until PHB... */
977
978 /* shift for setting TOS */
979 sopt <<= 2;
980 if(setsockopt(ep->sockfd,optlevel,optname,
981 (void*)&sopt,sizeof(sopt)) < 0){
982 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
983 "setsockopt(%s,%s=%d): %M",
984 ((optlevel==IPPROTO_IP)?
985 "IPPROTO_IP":"IPPROTO_IPV6"),
986 ((optname==IP_TOS)?"IP_TOS":"IPV6_TCLASS"),
987 sopt);
988 goto error;
989 }
990 }
991 }
992
993 /*
994 * Determine 'enddelay'. This is used to add a minimal delay
995 * for the sender before it sends the stop sessions message.
996 *
997 * If clocks are offset - and the sender side is ahead of the
998 * receiver side. The sender can send the stop sessions message
999 * before the reciever, and more importantly before the reciever
1000 * has waited 'timeout' after the last packet send time. In this
1001 * case, the reciever is required to shorten the session by removing
1002 * any packet records with a send time (relative to the receivers clock)
1003 * that was sent after stoptime-timeout. (i.e. the time the stop sessions
1004 * was recieved minus timeout. This ensures that a full 'timeout' period
1005 * has been waited after the time each and every packet is sent so that
1006 * duplicates and loss packet statistics are consistently determined.
1007 */
1008 if( (enddelayptr = OWPContextConfigGetV(cntrl->ctx,OWPEndDelay))){
1009 enddelay = *enddelayptr;
1010 }
1011
1012 ep->enddelay.tv_sec = trunc(enddelay);
1013 enddelay -= ep->enddelay.tv_sec;
1014 enddelay *= 1000000000;
1015 ep->enddelay.tv_nsec = trunc(enddelay);
1016
1017 tsession->endpoint = ep;
1018 *aval = OWP_CNTRL_ACCEPT;
1019 *err_ret = OWPErrOK;
1020 return True;
1021
1022 error:
1023 EndpointFree(ep,OWP_CNTRL_FAILURE);
1024 return False;
1025 }
1026
1027 static int owp_usr1;
1028 static int owp_usr2;
1029 static int owp_int;
1030
1031 /*
1032 * This sighandler is used to ensure SIGCHLD events are sent to this process.
1033 */
1034 static void
sig_nothing(int signo)1035 sig_nothing(
1036 int signo
1037 )
1038 {
1039 switch(signo){
1040 case SIGCHLD:
1041 break;
1042 default:
1043 OWPError(NULL,OWPErrFATAL,OWPErrUNKNOWN,
1044 "sig_nothing:Invalid signal(%d)",signo);
1045 exit(OWP_CNTRL_FAILURE);
1046 }
1047 return;
1048 }
1049
1050 static void
sig_catch(int signo)1051 sig_catch(
1052 int signo
1053 )
1054 {
1055 switch(signo){
1056 case SIGUSR1:
1057 owp_usr1 = 1;
1058 break;
1059 case SIGUSR2:
1060 owp_usr2 = 1;
1061 break;
1062 case SIGINT:
1063 owp_int = 1;
1064 break;
1065 case SIGALRM:
1066 break;
1067 default:
1068 OWPError(NULL,OWPErrFATAL,OWPErrUNKNOWN,
1069 "sig_catch:Invalid signal(%d)",signo);
1070 _exit(OWP_CNTRL_FAILURE);
1071 }
1072
1073 return;
1074 }
1075
1076 static void
skip(OWPEndpoint ep,uint32_t seq)1077 skip(
1078 OWPEndpoint ep,
1079 uint32_t seq
1080 )
1081 {
1082 _OWPSkip node;
1083
1084 /*
1085 * If this is the next seq in a current hole, increase the
1086 * hole size and return.
1087 */
1088 if(ep->tail_skip && (ep->tail_skip->sr.end + 1 == seq)){
1089 ep->tail_skip->sr.end = seq;
1090 return;
1091 }
1092
1093 if(!ep->free_skiplist){
1094 uint32_t i;
1095
1096 if(!(node = calloc(ep->num_allocskip,sizeof(_OWPSkipRec)))){
1097 OWPError(ep->cntrl->ctx,OWPErrFATAL,errno,
1098 "calloc(): %M");
1099 exit(OWP_CNTRL_UNAVAILABLE_TEMP);
1100 }
1101
1102 /* [0] is used to hold the malloc memory blocks list from
1103 * skip_allocated, and is not part of the "free" nodes
1104 * list.
1105 */
1106 node[0].next = ep->skip_allocated;
1107 ep->skip_allocated = node;
1108 /*
1109 * Now take the rest of the newly allocated nodes and make them
1110 * part of the "free" list.
1111 */
1112 for(i=1;i<ep->num_allocskip;i++){
1113 node[i].next = ep->free_skiplist;
1114 ep->free_skiplist = &node[i];
1115 }
1116 }
1117
1118 node = ep->free_skiplist;
1119 ep->free_skiplist = ep->free_skiplist->next;
1120
1121 node->sr.begin = node->sr.end = seq;
1122 node->next = NULL;
1123
1124 if(!ep->tail_skip){
1125 ep->tail_skip = ep->head_skip = node;
1126 }
1127 else{
1128 ep->tail_skip->next = node;
1129 ep->tail_skip = node;
1130 }
1131
1132 return;
1133 }
1134
1135 /*
1136 * HERE
1137 * Packet Formats:
1138 *
1139 * For unauthenticated mode:
1140 *
1141 * 0 1 2 3
1142 * 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
1143 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1144 * 00| Sequence Number |
1145 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1146 * 04| Timestamp |
1147 * 08| |
1148 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1149 * 12| Error Estimate | .
1150 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ .
1151 * . .
1152 * . Packet Padding .
1153 * . .
1154 * | |
1155 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1156 *
1157 *
1158 * For authenticated and encrypted modes:
1159 *
1160 * 0 1 2 3
1161 * 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
1162 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1163 * 00| Sequence Number |
1164 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1165 * 04| |
1166 * 08| MBZ (12 octets) |
1167 * 12| |
1168 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1169 * 16| Timestamp |
1170 * 20| |
1171 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1172 * 24| Error Estimate | |
1173 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1174 * 28| MBZ (6 octets) |
1175 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1176 * 32| |
1177 * 36| HMAC (16 octets) |
1178 * 40| |
1179 * 44| |
1180 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1181 * . .
1182 * . .
1183 * . Packet Padding .
1184 * . .
1185 * | |
1186 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1187 *
1188 */
1189
1190 /*
1191 * Function: run_sender
1192 *
1193 * Description:
1194 * This function is the main processing function for a "sender"
1195 * sub-process.
1196 *
1197 * In Args:
1198 *
1199 * Out Args:
1200 *
1201 * Scope:
1202 * Returns:
1203 * Side Effect:
1204 */
1205 static void
run_sender(OWPEndpoint ep)1206 run_sender(
1207 OWPEndpoint ep
1208 )
1209 {
1210 struct sockaddr *saddr;
1211 socklen_t saddrlen=0;
1212 char nodename[NI_MAXHOST];
1213 size_t nodenamelen = sizeof(nodename);
1214 char nodeserv[NI_MAXSERV];
1215 size_t nodeservlen = sizeof(nodeserv);
1216 uint32_t i;
1217 struct timespec currtime;
1218 struct timespec nexttime;
1219 struct timespec timeout;
1220 struct timespec latetime;
1221 struct timespec sleeptime;
1222 uint32_t esterror;
1223 uint32_t lasterror=0;
1224 uint8_t sync;
1225 ssize_t sent;
1226 uint32_t *seq;
1227 uint32_t clr_mem[8]; /* two blocks */
1228 char *clr_buffer = (char *)clr_mem; /* legal type pun ;) */
1229 uint8_t iv[16];
1230 char *padding;
1231 char *tstamp;
1232 char *tstamperr;
1233 char *hmac;
1234 OWPTimeStamp owptstamp;
1235 OWPNum64 nextoffset;
1236 _OWPSkip sr;
1237 uint32_t num_skiprecs;
1238 int r;
1239
1240 if( !(saddr = I2AddrSAddr(ep->remoteaddr,&saddrlen)) ||
1241 !I2AddrNodeName(ep->remoteaddr,nodename,&nodenamelen) ||
1242 !I2AddrServName(ep->remoteaddr,nodeserv,&nodeservlen)){
1243 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1244 "run_sender: Unable to extract saddr information");
1245 exit(OWP_CNTRL_FAILURE);
1246 }
1247
1248 /*
1249 * Initialize pointers to various positions in the packet buffer,
1250 * for data that changes for each packet. Also set zero padding.
1251 */
1252 memset(clr_buffer,0,32);
1253
1254 switch(ep->cntrl->mode){
1255 case OWP_MODE_OPEN:
1256 seq = (uint32_t*)&ep->payload[0];
1257 tstamp = &ep->payload[4];
1258 tstamperr = &ep->payload[12];
1259 hmac = NULL;
1260 padding = &ep->payload[14];
1261 break;
1262 case OWP_MODE_AUTHENTICATED:
1263 seq = (uint32_t*)&clr_buffer[0];
1264 tstamp = &ep->payload[16];
1265 tstamperr = &ep->payload[24];
1266 hmac = &ep->payload[32];
1267 padding = &ep->payload[48];
1268 break;
1269 case OWP_MODE_ENCRYPTED:
1270 seq = (uint32_t*)&clr_buffer[0];
1271 tstamp = &clr_buffer[16];
1272 tstamperr = &clr_buffer[24];
1273 hmac = &ep->payload[32];
1274 padding = &ep->payload[48];
1275 break;
1276 default:
1277 /*
1278 * things would have failed way earlier
1279 * but put default in to stop annoying
1280 * compiler warnings...
1281 */
1282 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1283 "run_sender: Bogus \"mode\" bits");
1284 exit(OWP_CNTRL_FAILURE);
1285 }
1286
1287 /*
1288 * initialize nextoffset (running sum of next sendtime relative to
1289 * start.
1290 */
1291 nextoffset = OWPULongToNum64(0);
1292 i=0;
1293
1294 /*
1295 * initialize tspec version of "timeout"
1296 */
1297 OWPNum64ToTimespec(&timeout,ep->tsession->test_spec.loss_timeout);
1298
1299 /*
1300 * Ensure schedule generation is starting at first packet in
1301 * series.
1302 */
1303 if(OWPScheduleContextReset(ep->tsession->sctx,NULL,NULL) != OWPErrOK){
1304 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1305 "ScheduleContextReset: FAILED");
1306 exit(OWP_CNTRL_FAILURE);
1307 }
1308
1309 do{
1310 /*
1311 * First setup "this" packet. calloc() used to allocate ep->payload,
1312 * so padding will already be zero. Just leave it if zero payload
1313 * is required.
1314 */
1315 #if !defined(OWP_ZERO_TEST_PAYLOAD)
1316 (void)I2RandomBytes(ep->cntrl->ctx->rand_src,(uint8_t *)padding,
1317 ep->tsession->test_spec.packet_size_padding);
1318 #endif
1319 nextoffset = OWPNum64Add(nextoffset,
1320 OWPScheduleContextGenerateNextDelta(
1321 ep->tsession->sctx));
1322 OWPNum64ToTimespec(&nexttime,nextoffset);
1323 timespecadd(&nexttime,&ep->start);
1324 *seq = htonl(i);
1325
1326 /*
1327 * blockEncrypt does CBC mode. Can still use this function for
1328 * both authenticated and encrypted mode because CBC with iv=0
1329 * of one block is identical to ECB of one block. Then iv is
1330 * ready for the next block in the case of encrypted mode.
1331 */
1332 RETRY:
1333 if(ep->cntrl->mode & OWP_MODE_DOCIPHER){
1334 /*
1335 * Initialize HMAC for this packet, and first block to it.
1336 */
1337 I2HMACSha1Init(ep->hmac_ctx,ep->hmac_key,sizeof(ep->hmac_key));
1338 I2HMACSha1Append(ep->hmac_ctx,(uint8_t *)&clr_buffer[0],16);
1339
1340 /*
1341 * Initialize IV and encrypt the first block
1342 */
1343 memset(iv,0,sizeof(iv));
1344 r = blockEncrypt(iv,&ep->aeskey,(uint8_t *)&clr_buffer[0],16*8,
1345 (uint8_t *)&ep->payload[0]);
1346 if(r != (16*8)){
1347 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1348 "run_sender: Invalid ECB encryption of seq (#%ul)",i);
1349 exit(OWP_CNTRL_FAILURE);
1350 }
1351 }
1352
1353 AGAIN:
1354 if(owp_int || owp_usr2){
1355 goto finish_sender;
1356 }
1357
1358 if(!_OWPGetTimespec(ep->cntrl->ctx,&currtime,&esterror,&sync)){
1359 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1360 "Problem retrieving time");
1361 exit(OWP_CNTRL_FAILURE);
1362 }
1363
1364 /*
1365 * If current time is greater than next send time...
1366 */
1367 if(timespeccmp(&currtime,&nexttime,>)){
1368
1369 /*
1370 * If current time is more than "timeout" past next
1371 * send time, then skip actually sending.
1372 */
1373 latetime = timeout;
1374 timespecadd(&latetime,&nexttime);
1375 if(timespeccmp(&currtime,&latetime,>)){
1376 skip(ep,i);
1377 goto SKIP_SEND;
1378 }
1379
1380 /* send-packet */
1381
1382 (void)OWPTimespecToTimestamp(&owptstamp,&currtime,
1383 &esterror,&lasterror);
1384 lasterror = esterror;
1385 owptstamp.sync = sync;
1386 _OWPEncodeTimeStamp((uint8_t *)tstamp,&owptstamp);
1387 if(!_OWPEncodeTimeStampErrEstimate((uint8_t *)tstamperr,
1388 &owptstamp)){
1389 OWPError(ep->cntrl->ctx,OWPErrFATAL,
1390 OWPErrUNKNOWN,
1391 "Invalid Timestamp Error");
1392 owptstamp.multiplier = 0xFF;
1393 owptstamp.scale = 0x3F;
1394 owptstamp.sync = 0;
1395 (void)_OWPEncodeTimeStampErrEstimate((uint8_t *)tstamperr,
1396 &owptstamp);
1397 }
1398
1399 /*
1400 * For ENCRYPTED mode, we have to encrypt the second
1401 * block after fetching the timestamp. (CBC mode)
1402 */
1403 if(ep->cntrl->mode & OWP_MODE_ENCRYPTED){
1404 /*
1405 * Append second block to HMAC (timestamp block)
1406 */
1407 I2HMACSha1Append(ep->hmac_ctx,(uint8_t *)&clr_buffer[16],16);
1408
1409 /*
1410 * Encrypt second block
1411 */
1412 r = blockEncrypt(iv,&ep->aeskey,(uint8_t *)&clr_buffer[16],16*8,
1413 (uint8_t *)&ep->payload[16]);
1414 if(r != (16*8)){
1415 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1416 "run_sender: Invalid CBC encryption of seq (#%ul)",
1417 i);
1418 exit(OWP_CNTRL_FAILURE);
1419 }
1420 }
1421
1422 if(hmac){
1423 uint8_t hmacd[I2HMAC_SHA1_DIGEST_SIZE];
1424
1425 memset(hmacd,0,sizeof(hmacd));
1426 I2HMACSha1Finish(ep->hmac_ctx,hmacd);
1427 memcpy(hmac,hmacd,MIN(16,I2HMAC_SHA1_DIGEST_SIZE));
1428 }
1429
1430 if(owp_int || owp_usr2){
1431 goto finish_sender;
1432 }
1433
1434 if( (sent = sendto(ep->sockfd,ep->payload,
1435 ep->len_payload,0,saddr,saddrlen)) < 0){
1436 switch(errno){
1437 /* retry errors */
1438 case ENOBUFS:
1439 goto RETRY;
1440 break;
1441 /* fatal errors */
1442 case EBADF:
1443 case EACCES:
1444 case ENOTSOCK:
1445 case EFAULT:
1446 case EAGAIN:
1447 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1448 "Unable to send([%s]:%s:(#%d): %M",
1449 nodename,nodeserv,i);
1450 exit(OWP_CNTRL_FAILURE);
1451 break;
1452 /* ignore everything else */
1453 default:
1454 break;
1455 }
1456
1457 /* but do note it as INFO for debugging */
1458 OWPError(ep->cntrl->ctx,OWPErrDEBUG,OWPErrUNKNOWN,
1459 "Unable to send([%s]:%s:(#%d): %M",
1460 nodename,nodeserv,i);
1461 }
1462
1463 SKIP_SEND:
1464 i++;
1465 }
1466 else{
1467 /*
1468 * Sleep until we should send the next packet.
1469 */
1470
1471 sleeptime = nexttime;
1472 timespecsub(&sleeptime,&currtime);
1473 if((nanosleep(&sleeptime,NULL) == 0) || (errno == EINTR)){
1474 goto AGAIN;
1475 }
1476 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1477 "nanosleep(%u.%u,nil): %M",
1478 sleeptime.tv_sec,sleeptime.tv_nsec);
1479 exit(OWP_CNTRL_FAILURE);
1480 }
1481
1482 } while(i < ep->tsession->test_spec.npackets);
1483
1484 /*
1485 * Wait until lossthresh after last packet or
1486 * for a signal to exit.
1487 * (nexttime currently holds the time for the last packet sent, so
1488 * just add loss_timeout. Round up to the next second since I'm lazy.)
1489 */
1490 #if OLD
1491 nexttime.tv_sec += (int)OWPNum64ToDouble(
1492 ep->tsession->test_spec.loss_timeout)+1;
1493 #endif
1494 latetime = timeout;
1495 timespecadd(&latetime,&nexttime);
1496 timespecadd(&latetime,&ep->enddelay);
1497
1498 while(!owp_usr2 && !owp_int){
1499 if(!_OWPGetTimespec(ep->cntrl->ctx,&currtime,&esterror,&sync)){
1500 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1501 "Problem retrieving time");
1502 exit(OWP_CNTRL_FAILURE);
1503 }
1504
1505 if(timespeccmp(&latetime,&currtime,<))
1506 break;
1507
1508 sleeptime = latetime;
1509 timespecsub(&sleeptime,&currtime);
1510 #if NOT
1511 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1512 "run_sender: end nanosleep(%lu.%lu,nil)",
1513 sleeptime.tv_sec,sleeptime.tv_nsec);
1514 #endif
1515 if(nanosleep(&sleeptime,NULL) == 0)
1516 break;
1517 if(errno != EINTR){
1518 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1519 "nanosleep(%u.%u,nil): %M",
1520 sleeptime.tv_sec,sleeptime.tv_nsec);
1521 exit(OWP_CNTRL_FAILURE);
1522 }
1523 }
1524
1525 finish_sender:
1526 if(owp_int){
1527 OWPError(ep->cntrl->ctx,OWPErrINFO,OWPErrUNKNOWN,
1528 "run_sender: Exiting from signal");
1529 exit(OWP_CNTRL_FAILURE);
1530 }
1531
1532 /*
1533 * Save session information into IPC file so parent can
1534 * see results.
1535 */
1536 for(num_skiprecs=0,sr = ep->head_skip; sr; sr = sr->next,num_skiprecs++);
1537
1538 #if USE_SHMIPC
1539 /*
1540 * If SHMIPC, then the size for the file needs to be
1541 * specified before writing.
1542 */
1543 if( (ftruncate(ep->skiprecfd,
1544 (off_t)(8 + (_OWP_SKIPREC_SIZE * num_skiprecs))) != 0)){
1545 OWPError(ep->cntrl->ctx,OWPErrFATAL,errno,
1546 "Sizing shared-mem: ftruncate(): %M");
1547 exit(OWP_CNTRL_FAILURE);
1548 }
1549 #endif
1550
1551 /*
1552 * send (i = nextseq, skip records) to control process
1553 * for inclusion in StopSessions message...
1554 * Use network byte order so the data from the fd can just
1555 * be copied into the StopSessions message. (Besides, this
1556 * allows the control portion of the server to be on a different
1557 * architecture than the sender if this is ever extended to an
1558 * rpc model.)
1559 *
1560 */
1561
1562 /* save "Next Seqno" */
1563 i = htonl(i);
1564 if(I2Writeni(ep->skiprecfd,&i,4,&owp_int) != 4){
1565 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1566 "run_sender: I2Writeni(): %M");
1567 exit(OWP_CNTRL_FAILURE);
1568 }
1569
1570 /* save "Num Skip Records" */
1571 num_skiprecs = htonl(num_skiprecs);
1572 if(I2Writeni(ep->skiprecfd,&num_skiprecs,4,&owp_int) != 4){
1573 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1574 "run_sender: I2Writeni(): %M");
1575 exit(OWP_CNTRL_FAILURE);
1576 }
1577
1578 /*
1579 * Now save the skip records.
1580 */
1581 for(sr = ep->head_skip; sr; sr = sr->next){
1582 uint8_t skipmsg[_OWP_SKIPREC_SIZE];
1583
1584 _OWPEncodeSkipRecord((uint8_t *)skipmsg,&sr->sr);
1585 if(I2Writeni(ep->skiprecfd,skipmsg,_OWP_SKIPREC_SIZE,&owp_int) !=
1586 _OWP_SKIPREC_SIZE){
1587 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1588 "run_sender: I2Writeni(): %M");
1589 exit(OWP_CNTRL_FAILURE);
1590 }
1591 }
1592
1593 exit(OWP_CNTRL_ACCEPT);
1594 }
1595
1596
1597 static OWPLostPacket
alloc_node(OWPEndpoint ep,uint32_t seq)1598 alloc_node(
1599 OWPEndpoint ep,
1600 uint32_t seq
1601 )
1602 {
1603 OWPLostPacket node;
1604 I2Datum k,v;
1605
1606 if((seq >= ep->tsession->test_spec.npackets) ||
1607 (ep->end && (seq <= ep->end->seq))){
1608 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
1609 "Invalid seq number for OWPLostPacket buf");
1610 return NULL;
1611 }
1612
1613 if(!ep->freelist){
1614 uint32_t i;
1615
1616 OWPError(ep->cntrl->ctx,OWPErrDEBUG,OWPErrUNKNOWN,
1617 "alloc_node: Pre-alloc buffer too small. Allocating additional nodes for lost-packet-buffer.");
1618 if(!(node = calloc(ep->numalist,sizeof(OWPLostPacketRec)))){
1619 OWPError(ep->cntrl->ctx,OWPErrFATAL,errno,
1620 "calloc(): %M");
1621 return NULL;
1622 }
1623
1624 node[0].next = ep->lost_allocated;
1625 ep->lost_allocated = node;
1626 for(i=1;i<ep->numalist;i++){
1627 node[i].next = ep->freelist;
1628 ep->freelist = &node[i];
1629 }
1630 }
1631
1632 node = ep->freelist;
1633 ep->freelist = ep->freelist->next;
1634
1635 node->seq = seq;
1636 node->hit = 0;
1637 node->next = NULL;
1638
1639 k.dptr = &node->seq;
1640 k.dsize = sizeof(node->seq);
1641 v.dptr = node;
1642 v.dsize = sizeof(*node);
1643
1644 if(I2HashStore(ep->lost_packet_buffer,k,v) != 0){
1645 return NULL;
1646 }
1647
1648 return node;
1649 }
1650
1651 static void
free_node(OWPEndpoint ep,OWPLostPacket node)1652 free_node(
1653 OWPEndpoint ep,
1654 OWPLostPacket node
1655 )
1656 {
1657 I2Datum k;
1658
1659 k.dptr = &node->seq;
1660 k.dsize = sizeof(node->seq);
1661
1662 if(I2HashDelete(ep->lost_packet_buffer,k) != 0){
1663 OWPError(ep->cntrl->ctx,OWPErrWARNING,OWPErrUNKNOWN,
1664 "I2HashDelete: Unable to remove seq #%lu from lost-packet hash",
1665 node->seq);
1666 }
1667
1668 node->next = ep->freelist;
1669 ep->freelist = node;
1670
1671 return;
1672 }
1673
1674 static OWPLostPacket
get_node(OWPEndpoint ep,uint32_t seq)1675 get_node(
1676 OWPEndpoint ep,
1677 uint32_t seq
1678 )
1679 {
1680 OWPLostPacket node;
1681 I2Datum k,v;
1682
1683 /*
1684 * optimize for most frequent case.
1685 */
1686 if(seq == ep->end->seq){
1687 return ep->end;
1688 }
1689
1690 /*
1691 * Need to build the list from current "end" to this number.
1692 */
1693 if(seq > ep->end->seq){
1694 node = ep->end;
1695
1696 while(node->seq < seq){
1697 OWPTimeStamp abs;
1698
1699 node->next = alloc_node(ep,node->seq+1);
1700 node->next->relative = OWPNum64Add(node->relative,
1701 OWPScheduleContextGenerateNextDelta(
1702 ep->tsession->sctx));
1703 node = node->next;
1704
1705 abs.owptime = OWPNum64Add(node->relative,
1706 ep->tsession->test_spec.start_time);
1707 (void)OWPTimestampToTimespec(&node->absolute,&abs);
1708 }
1709
1710 ep->end = node;
1711
1712 return node;
1713 }
1714
1715 /*
1716 * Shouldn't be requesting this seq number... It should already
1717 * be loss_timeout in the past.
1718 */
1719 if(seq < ep->begin->seq){
1720 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrINVALID,
1721 "Invalid seq number request");
1722 return NULL;
1723 }
1724
1725 /*
1726 * seq requested in within the begin<->end range, just fetch from
1727 * hash.
1728 */
1729 k.dptr = &seq;
1730 k.dsize = sizeof(seq);
1731
1732 if(!I2HashFetch(ep->lost_packet_buffer,k,&v)){
1733 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
1734 "Unable to fetch from lost-packet-buffer");
1735 return NULL;
1736 }
1737
1738 return (OWPLostPacket)v.dptr;
1739 }
1740
1741 static ssize_t
recvfromttl(OWPContext ctx,int sockfd,void * buf,size_t buf_len,struct sockaddr * local,socklen_t local_len,struct sockaddr * peer,socklen_t * peer_len,uint8_t * ttl)1742 recvfromttl(
1743 OWPContext ctx,
1744 int sockfd,
1745 void *buf,
1746 size_t buf_len,
1747 struct sockaddr *local,
1748 socklen_t local_len __attribute__((unused)),
1749 struct sockaddr *peer,
1750 socklen_t *peer_len,
1751 uint8_t *ttl
1752 )
1753 {
1754 struct msghdr msg;
1755 struct iovec iov[1];
1756 ssize_t rc;
1757 struct cmsghdr *cmdmsgptr;
1758 union {
1759 struct cmsghdr cm;
1760 char control[CMSG_SPACE(sizeof(uint8_t))];
1761 } cmdmsgdata;
1762
1763 *ttl = 255; /* initialize to default value */
1764
1765 iov[0].iov_base = buf;
1766 iov[0].iov_len = buf_len;
1767
1768 memset(&msg,0,sizeof(msg));
1769 msg.msg_name = peer;
1770 msg.msg_namelen = *peer_len;
1771 msg.msg_iov = iov;
1772 msg.msg_iovlen = 1;
1773 msg.msg_control = &cmdmsgdata;
1774 msg.msg_controllen = sizeof(cmdmsgdata.control);
1775 msg.msg_flags = 0;
1776
1777 if((rc = recvmsg(sockfd,&msg,0)) < 0){
1778 return rc;
1779 }
1780
1781 *peer_len = msg.msg_namelen;
1782
1783 if((msg.msg_controllen < sizeof(struct cmsghdr)) ||
1784 (msg.msg_flags & MSG_CTRUNC)){
1785 return rc;
1786 }
1787
1788 for(cmdmsgptr = CMSG_FIRSTHDR(&msg);
1789 (cmdmsgptr);
1790 cmdmsgptr = CMSG_NXTHDR(&msg,cmdmsgptr)){
1791 switch(local->sa_family){
1792 #ifdef AF_INET6
1793 case AF_INET6:
1794 #ifdef IPV6_HOPLIMIT
1795 if(cmdmsgptr->cmsg_level == IPPROTO_IPV6 &&
1796 cmdmsgptr->cmsg_type == IPV6_HOPLIMIT){
1797 /*
1798 * IPV6_HOPLIMIT is defined as an int, type coercion
1799 * will convert it to a uint8_t.
1800 */
1801 *ttl = *(int *)CMSG_DATA(cmdmsgptr);
1802 goto NEXTCMSG;
1803 }
1804 #endif
1805 break;
1806 #endif
1807 case AF_INET:
1808 /*
1809 * FreeBSD and OS X seem to use IP_RECVTTL. Linux
1810 * seems to use IP_TTL - but still has IP_RECVTTL
1811 * defined.
1812 *
1813 * Gotta love standards...
1814 *
1815 * (Looking at opendarwin kernel sources leads me to
1816 * believe that IP_RECVTTL is a uchar and actual
1817 * documentation in the CMSG man page on Linux
1818 * tells me IP_TTL should be treated as an int. But,
1819 * I have never really found the 'definitive' standard
1820 * for this stuff. oh well...)
1821 */
1822 #ifdef IP_RECVTTL
1823 if(cmdmsgptr->cmsg_level == IPPROTO_IP &&
1824 cmdmsgptr->cmsg_type == IP_RECVTTL){
1825 *ttl = *(uint8_t *)CMSG_DATA(cmdmsgptr);
1826 goto NEXTCMSG;
1827 }
1828 else
1829 #endif
1830 if(cmdmsgptr->cmsg_level == IPPROTO_IP &&
1831 cmdmsgptr->cmsg_type == IP_TTL){
1832 *ttl = *(int *)CMSG_DATA(cmdmsgptr);
1833 goto NEXTCMSG;
1834 }
1835 break;
1836 default:
1837 OWPError(ctx,OWPErrFATAL,OWPErrINVALID,
1838 "Invalid address family for test");
1839 return -rc;
1840 }
1841
1842 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
1843 "recvfromttl: Unknown ancillary data, len = %d, level = %d, type = %d",
1844 cmdmsgptr->cmsg_len, cmdmsgptr->cmsg_level,
1845 cmdmsgptr->cmsg_type);
1846 return -rc;
1847 NEXTCMSG:
1848 ;
1849 }
1850
1851 return rc;
1852 }
1853
1854 /*
1855 * Function: flush_lost
1856 *
1857 * Description:
1858 *
1859 * In Args:
1860 *
1861 * Out Args:
1862 *
1863 * Scope:
1864 * Returns:
1865 * < 0: error
1866 * > 0: test is complete
1867 * = 0: successfully flushed as much as needed
1868 * Side Effect:
1869 */
1870 static int
flush_lost(OWPEndpoint ep,struct timespec * ctime,struct timespec * ltime,OWPTimeStamp * errest)1871 flush_lost(
1872 OWPEndpoint ep,
1873 struct timespec *ctime,
1874 struct timespec *ltime,
1875 OWPTimeStamp *errest
1876 )
1877 {
1878 OWPLostPacket node;
1879 struct timespec currtime = *ctime;
1880 struct timespec lostspec = *ltime;
1881 struct timespec expectspec;
1882
1883 /*
1884 * Set expectspec to the time the oldest (begin) packet
1885 * in the missing packet queue should be declared lost.
1886 */
1887 timespecclear(&expectspec);
1888 timespecadd(&expectspec,&ep->begin->absolute);
1889 timespecadd(&expectspec,&lostspec);
1890
1891 /*
1892 * If owp_usr2, then StopSessions has been received. We
1893 * need to flush all records for the test now. So, artificailly
1894 * set currtime to a time greater than expectspec so the loop
1895 * will continue until all packet records are flushed.
1896 * XXX:
1897 * This is an over-kill solution to ensure missing
1898 * packet records are in the session. A better solution
1899 * would only flush record up through the "Next Seqno"
1900 * field passed in the StopSessions message from the
1901 * sender. But, since that is not available in this
1902 * process- and we have no way of knowing just how far offset the
1903 * sender/receiver clocks are, this is the temporary
1904 * fix. Unneeded records will be deleted by the parent.
1905 */
1906 if(owp_usr2){
1907 timespecclear(&currtime);
1908 timespecadd(&currtime,&expectspec);
1909 timespecadd(&currtime,&lostspec);
1910 }
1911
1912 /*
1913 * Flush the missing packet buffer. Output missing packet
1914 * records along the way.
1915 */
1916 while(timespeccmp(&expectspec,&currtime,<)){
1917
1918 /*
1919 * If !hit - and the seq number is less than
1920 * npackets, then output a "missing packet" record.
1921 * (seq number could be greater than or equal to
1922 * npackets if it takes longer than "timeout" for
1923 * the stopsessions message to get to us. We could
1924 * already have missing packet records in our
1925 * queue.)
1926 */
1927 if(!ep->begin->hit){
1928 OWPDataRec lostrec;
1929
1930 /*
1931 * set fields in lostrec for missing packet
1932 * record.
1933 */
1934 /* seq no */
1935 lostrec.seq_no = ep->begin->seq;
1936 /* presumed sent time */
1937 lostrec.send.owptime = OWPNum64Add(
1938 ep->tsession->test_spec.start_time,
1939 ep->begin->relative);
1940 lostrec.send.sync = 0;
1941 lostrec.send.multiplier = 1;
1942 lostrec.send.scale = 64;
1943
1944 /* special value recv time */
1945 lostrec.recv = *errest;
1946 lostrec.recv.owptime = OWPULongToNum64(0);
1947
1948 /* recv error was set above... */
1949
1950 lostrec.ttl = 255;
1951
1952 if( !OWPWriteDataRecord(ep->cntrl->ctx,
1953 ep->datafile,&lostrec)){
1954 OWPError(ep->cntrl->ctx,OWPErrFATAL,
1955 OWPErrUNKNOWN,
1956 "OWPWriteDataRecord()");
1957 return -1;
1958 }
1959 }
1960
1961 /*
1962 * Pop the front off the queue.
1963 */
1964 node = ep->begin;
1965
1966 if(ep->begin->next){
1967 ep->begin = ep->begin->next;
1968 }
1969 else if((ep->begin->seq+1) < ep->tsession->test_spec.npackets){
1970 ep->begin = get_node(ep,ep->begin->seq+1);
1971 }
1972 else{
1973 free_node(ep,node);
1974 ep->begin = ep->end = NULL;
1975 return 1;
1976 }
1977 free_node(ep,node);
1978
1979 timespecclear(&expectspec);
1980 timespecadd(&expectspec,&ep->begin->absolute);
1981 timespecadd(&expectspec,&lostspec);
1982
1983 /*
1984 * StopSessions received: fast-forward currtime
1985 */
1986 if(owp_usr2){
1987 timespecclear(&currtime);
1988 timespecadd(&currtime,&expectspec);
1989 timespecadd(&currtime,&lostspec);
1990 }
1991 }
1992
1993 return 0;
1994 }
1995
1996 static void
run_receiver(OWPEndpoint ep)1997 run_receiver(
1998 OWPEndpoint ep
1999 )
2000 {
2001 double fudge;
2002 struct timespec currtime;
2003 struct timespec fudgespec;
2004 struct timespec lostspec;
2005 struct itimerval wake;
2006 uint32_t *seq;
2007 char *tstamp;
2008 char *tstamperr;
2009 char *hmac;
2010 uint32_t esterror,lasterror=0;
2011 uint8_t sync;
2012 OWPTimeStamp expecttime;
2013 OWPSessionHeaderRec hdr;
2014 uint8_t lostrec[_OWP_DATAREC_SIZE];
2015 OWPLostPacket node;
2016 int owp_intr;
2017 uint32_t finished = OWP_SESSION_FINISHED_INCOMPLETE;
2018 OWPDataRec datarec;
2019 struct sockaddr *lsaddr;
2020 socklen_t lsaddrlen;
2021 struct sockaddr *rsaddr;
2022 socklen_t rsaddrlen;
2023 int rc;
2024
2025 /*
2026 * Prepare the file header - had to wait until now to
2027 * get the real starttime.
2028 */
2029 memset(&hdr,0,sizeof(hdr));
2030 hdr.finished = finished;
2031 memcpy(&hdr.sid,ep->tsession->sid,sizeof(hdr.sid));
2032
2033 if( !(lsaddr = I2AddrSAddr(ep->tsession->sender,&lsaddrlen))){
2034 exit(OWP_CNTRL_FAILURE);
2035 }
2036 memcpy(&hdr.addr_sender,lsaddr,lsaddrlen);
2037
2038 if( !(lsaddr = I2AddrSAddr(ep->tsession->receiver,&lsaddrlen))){
2039 exit(OWP_CNTRL_FAILURE);
2040 }
2041 memcpy(&hdr.addr_receiver,lsaddr,lsaddrlen);
2042
2043 hdr.conf_sender = ep->tsession->conf_sender;
2044 hdr.conf_receiver = ep->tsession->conf_receiver;
2045 hdr.test_spec = ep->tsession->test_spec;
2046
2047 /*
2048 * Write the file header.
2049 */
2050 if( !OWPWriteDataHeader(ep->cntrl->ctx,ep->datafile,&hdr)){
2051 exit(OWP_CNTRL_FAILURE);
2052 }
2053
2054 /*
2055 * Get pointer to lsaddr used for listening.
2056 */
2057 if( !(lsaddr = I2AddrSAddr(ep->localaddr,&lsaddrlen))){
2058 exit(OWP_CNTRL_FAILURE);
2059 }
2060 /*
2061 * Get pointer to rsaddr used to verify peer.
2062 */
2063 if( !(rsaddr = I2AddrSAddr(ep->remoteaddr,&rsaddrlen))){
2064 exit(OWP_CNTRL_FAILURE);
2065 }
2066
2067 /*
2068 * Initialize pointers to various positions in the packet buffer.
2069 * (useful for the different "modes".)
2070 */
2071 seq = (uint32_t*)&ep->payload[0];
2072 switch(ep->cntrl->mode){
2073 case OWP_MODE_OPEN:
2074 tstamp = &ep->payload[4];
2075 tstamperr = &ep->payload[12];
2076 hmac = NULL;
2077 break;
2078 case OWP_MODE_ENCRYPTED:
2079 case OWP_MODE_AUTHENTICATED:
2080 tstamp = &ep->payload[16];
2081 tstamperr = &ep->payload[24];
2082 hmac = &ep->payload[32];
2083 break;
2084 default:
2085 /*
2086 * things would have failed way earlier
2087 * but putting default in to stop annoying
2088 * compiler warnings...
2089 */
2090 exit(OWP_CNTRL_FAILURE);
2091 }
2092
2093 /*
2094 * Initialize the buffer used to report "lost" packets.
2095 */
2096 memset(lostrec,0,_OWP_DATAREC_SIZE);
2097
2098 /*
2099 * Get the "average" packet interval. I use this
2100 * to set the wake up timer to MIN(2*packet_interval,1) past the
2101 * time it can be declared lost. (lets call this fudgespec)
2102 * With luck, this will allow the next received packet to be the
2103 * event that wakes up the process, instead of the timer. However,
2104 * I never let this be greater than 1 second so that small
2105 * packet rates still produce data at the expected rate.
2106 * (This basically sets things up so the recv process will wake up
2107 * 1 second past the "end-of-test" to declare it over. In most cases,
2108 * the sender will already have sent the StopSession message, so
2109 * that event will actually wake the process up instead of the
2110 * timer.)
2111 */
2112 fudge = 2.0/OWPTestPacketRate(ep->cntrl->ctx,&ep->tsession->test_spec);
2113 fudge = MIN(fudge,1.0);
2114 /* just using expecttime as a temp var here. */
2115 expecttime.owptime = OWPDoubleToNum64(fudge);
2116 OWPNum64ToTimespec(&fudgespec,expecttime.owptime);
2117
2118 /*
2119 * get a timespec version of loss_timeout
2120 */
2121 OWPNum64ToTimespec(&lostspec,ep->tsession->test_spec.loss_timeout);
2122
2123 /*
2124 * Ensure schedule generation is starting at first packet in
2125 * series.
2126 */
2127 if(OWPScheduleContextReset(ep->tsession->sctx,NULL,NULL) != OWPErrOK){
2128 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2129 "ScheduleContextReset FAILED!");
2130 exit(OWP_CNTRL_FAILURE);
2131 }
2132 /*
2133 * Initialize list with first node
2134 */
2135 ep->begin = ep->end = alloc_node(ep,0);
2136 if(!ep->begin){
2137 goto error;
2138 }
2139
2140 ep->begin->relative = OWPScheduleContextGenerateNextDelta(
2141 ep->tsession->sctx);
2142 /* just using expecttime as a temp var here. */
2143 expecttime.owptime = OWPNum64Add(ep->begin->relative,
2144 ep->tsession->test_spec.start_time);
2145 (void)OWPTimestampToTimespec(&ep->begin->absolute,&expecttime);
2146
2147 /*
2148 * initialize currtime for absolute to relative time conversion
2149 * needed by timers.
2150 */
2151 if(!_OWPGetTimespec(ep->cntrl->ctx,&currtime,&esterror,&sync)){
2152 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2153 "Problem retrieving time");
2154 goto error;
2155 }
2156
2157 /*
2158 * Save that time as a timestamp
2159 */
2160 (void)OWPTimespecToTimestamp(&datarec.recv,&currtime,&esterror,&lasterror);
2161 lasterror = esterror;
2162 datarec.recv.sync = sync;
2163
2164 rc = flush_lost(ep,&currtime,&lostspec,&datarec.recv);
2165 if(rc < 0){
2166 goto error;
2167 }
2168 else if(rc > 0){
2169 goto test_over;
2170 }
2171
2172 while(1){
2173 struct sockaddr_storage peer_addr;
2174 socklen_t peer_addr_len;
2175 again:
2176 /*
2177 * set itimer to go off just past loss_timeout after the time
2178 * for the last seq number in the list. Adding "fudge" so we
2179 * don't wake up anymore than really necessary.
2180 * (With luck, a received packet will actually wake this up,
2181 * and not the timer.)
2182 */
2183 tvalclear(&wake.it_value);
2184 timespecadd((struct timespec*)&wake.it_value,
2185 &ep->end->absolute);
2186 timespecadd((struct timespec*)&wake.it_value,&lostspec);
2187 timespecadd((struct timespec*)&wake.it_value,&fudgespec);
2188 timespecsub((struct timespec*)&wake.it_value,&currtime);
2189
2190 wake.it_value.tv_usec /= 1000; /* convert nsec to usec */
2191 tvalclear(&wake.it_interval);
2192
2193 /*
2194 * Set the timer.
2195 */
2196 owp_intr = 0;
2197 if(setitimer(ITIMER_REAL,&wake,NULL) != 0){
2198 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2199 "setitimer(wake=%d,%d) seq=%lu: %M",
2200 wake.it_value.tv_sec,wake.it_value.tv_usec,
2201 ep->end->seq);
2202 goto error;
2203 }
2204
2205 if(owp_int){
2206 goto error;
2207 }
2208 if(owp_usr2){
2209 goto test_over;
2210 }
2211
2212 peer_addr_len = sizeof(peer_addr);
2213 memset(&peer_addr,0,sizeof(peer_addr));
2214 if(!owp_usr2 &&
2215 (recvfromttl(ep->cntrl->ctx,ep->sockfd,
2216 ep->payload,ep->len_payload,lsaddr,lsaddrlen,
2217 (struct sockaddr*)&peer_addr,&peer_addr_len,
2218 &datarec.ttl) != (ssize_t)ep->len_payload)){
2219 if(errno != EINTR){
2220 OWPError(ep->cntrl->ctx,OWPErrFATAL,
2221 OWPErrUNKNOWN,"recvfromttl(): %M");
2222 goto error;
2223 }
2224 owp_intr = 1;
2225 }
2226
2227 if(owp_int){
2228 goto error;
2229 }
2230 if(owp_usr2){
2231 goto test_over;
2232 }
2233
2234 /*
2235 * Fetch time before ANYTHING else to minimize time errors.
2236 */
2237 if(!_OWPGetTimespec(ep->cntrl->ctx,&currtime,&esterror,&sync)){
2238 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2239 "Problem retrieving time");
2240 goto error;
2241 }
2242
2243 /*
2244 * Check signals...
2245 */
2246 if(owp_int){
2247 goto error;
2248 }
2249 if(owp_usr2){
2250 goto test_over;
2251 }
2252
2253 /*
2254 * Save that time as a timestamp
2255 */
2256 (void)OWPTimespecToTimestamp(&datarec.recv,&currtime,
2257 &esterror,&lasterror);
2258 lasterror = esterror;
2259 datarec.recv.sync = sync;
2260
2261 rc = flush_lost(ep,&currtime,&lostspec,&datarec.recv);
2262 if(rc < 0){
2263 goto error;
2264 }
2265 else if(rc > 0){
2266 goto test_over;
2267 }
2268
2269 /*
2270 * Check signals...
2271 */
2272 if(owp_int){
2273 goto error;
2274 }
2275 if(owp_usr2){
2276 goto test_over;
2277 }
2278 if(owp_intr){
2279 goto again;
2280 }
2281
2282 /*
2283 * Verify peer before looking at packet.
2284 */
2285 if(I2SockAddrEqual(rsaddr,rsaddrlen,
2286 (struct sockaddr*)&peer_addr,
2287 peer_addr_len,I2SADDR_ALL) <= 0){
2288 goto again;
2289 }
2290
2291 /*
2292 * Decrypt the packet if needed.
2293 */
2294 if(ep->cntrl->mode & OWP_MODE_DOCIPHER){
2295 uint8_t iv[16];
2296 int r;
2297 uint8_t hmacd[I2HMAC_SHA1_DIGEST_SIZE];
2298
2299 /*
2300 * Initialize HMAC and iv.
2301 */
2302 memset(iv,0,sizeof(iv));
2303 I2HMACSha1Init(ep->hmac_ctx,ep->hmac_key,sizeof(ep->hmac_key));
2304
2305 /*
2306 * Decrypt first block
2307 */
2308 r = blockDecrypt(iv,&ep->aeskey,(uint8_t *)&ep->payload[0],
2309 16*8,(uint8_t *)&ep->payload[0]);
2310 if(r != (16*8)){
2311 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2312 "run_receiver: Invalid ECB decryption");
2313 goto error;
2314 }
2315 I2HMACSha1Append(ep->hmac_ctx,(uint8_t *)&ep->payload[0],16);
2316
2317
2318 if(ep->cntrl->mode & OWP_MODE_ENCRYPTED){
2319 /*
2320 * Decrypt second block if full encrypted mode wanted
2321 * (CBC mode done by blockDecrypt)
2322 */
2323 r = blockDecrypt(iv,&ep->aeskey,(uint8_t *)&ep->payload[16],
2324 16*8,(uint8_t *)&ep->payload[16]);
2325 if(r != (16*8)){
2326 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2327 "run_receiver: Invalid CBC decryption");
2328 goto error;
2329 }
2330 I2HMACSha1Append(ep->hmac_ctx,(uint8_t *)&ep->payload[16],16);
2331 }
2332
2333 memset(hmacd,0,sizeof(hmacd));
2334 I2HMACSha1Finish(ep->hmac_ctx,hmacd);
2335 if( (memcmp(hmac,hmacd,
2336 MIN(_OWP_RIJNDAEL_BLOCK_SIZE,sizeof(hmacd))) != 0)){
2337 OWPError(ep->cntrl->ctx,OWPErrWARNING,OWPErrUNKNOWN,
2338 "run_receiver: Invalid HMAC on received packet: "
2339 "ignoring");
2340 goto again;
2341 }
2342 }
2343
2344 datarec.seq_no = ntohl(*seq);
2345 if(datarec.seq_no >= ep->tsession->test_spec.npackets){
2346 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2347 "run_recv: Invalid seq_no received: %lu",datarec.seq_no);
2348 goto error;
2349 }
2350 #if NOT
2351 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2352 "run_recv: seq_no received: %lu",datarec.seq_no);
2353 #endif
2354
2355 /*
2356 * If it is no-longer in the buffer, than we ignore
2357 * it.
2358 */
2359 if(datarec.seq_no < ep->begin->seq)
2360 goto again;
2361
2362 /*
2363 * What time did we expect the sender to send the packet?
2364 */
2365 if(!(node = get_node(ep,datarec.seq_no))){
2366 goto error;
2367 }
2368 (void)OWPTimespecToTimestamp(&expecttime,&node->absolute,
2369 NULL,NULL);
2370 /*
2371 * What time did sender send this packet?
2372 */
2373 _OWPDecodeTimeStamp(&datarec.send,(uint8_t *)tstamp);
2374 if(!_OWPDecodeTimeStampErrEstimate(&datarec.send,(uint8_t *)tstamperr)){
2375 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2376 "Invalid send timestamp!");
2377 goto error;
2378 }
2379
2380 /*
2381 * Now we can start the validity tests from Section 4.2 of
2382 * the spec...
2383 * MUST discard if:
2384 */
2385
2386 /*
2387 * 1.
2388 * Send timestamp is more than timeout in past or future.
2389 * (i.e. send/recv differ by more than "timeout")
2390 */
2391 if(OWPNum64Diff(datarec.send.owptime,datarec.recv.owptime) >
2392 ep->tsession->test_spec.loss_timeout){
2393 goto again;
2394 }
2395 #if NOT
2396 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2397 "run_recv: seq_no passed 1: %lu",datarec.seq_no);
2398 #endif
2399
2400 /*
2401 * 2.
2402 * Send timestamp differs by more than "timeout" from
2403 * "scheduled" send time.
2404 */
2405 if(OWPNum64Diff(datarec.send.owptime,expecttime.owptime) >
2406 ep->tsession->test_spec.loss_timeout){
2407 goto again;
2408 }
2409 #if NOT
2410 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2411 "run_recv: seq_no passed 2: %lu",datarec.seq_no);
2412 #endif
2413
2414 /*
2415 * Made it through all validity tests. Record the packet!
2416 */
2417 node->hit = True;
2418
2419 if( !OWPWriteDataRecord(ep->cntrl->ctx,ep->datafile,
2420 &datarec)){
2421 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2422 "OWPWriteDataRecord()");
2423 goto error;
2424 }
2425 #if NOT
2426 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2427 "run_recv: seq_no recorded: %lu",datarec.seq_no);
2428 #endif
2429 }
2430
2431 test_over:
2432
2433 /*
2434 * Set the "finished" bit in the file to "incomplete". The parent
2435 * process will change this to "normal" after evaluating the
2436 * data from the stop sessions message.
2437 */
2438 if( !_OWPWriteDataHeaderFinished(ep->cntrl->ctx,ep->datafile,finished,0)){
2439 goto error;
2440 }
2441 fclose(ep->datafile);
2442 ep->datafile = NULL;
2443
2444 exit(OWP_CNTRL_ACCEPT);
2445
2446 error:
2447
2448 if(ep->datafile){
2449 (void)_OWPWriteDataHeaderFinished(ep->cntrl->ctx,ep->datafile,
2450 OWP_SESSION_FINISHED_ERROR,0);
2451 fclose(ep->datafile);
2452 }
2453
2454 if(ep->userfile && (strlen(ep->fname) > 0)){
2455 unlink(ep->fname);
2456 }
2457
2458 if(owp_int){
2459 OWPError(ep->cntrl->ctx,OWPErrINFO,OWPErrUNKNOWN,
2460 "run_receiver: Exiting from signal");
2461 }
2462
2463 exit(OWP_CNTRL_FAILURE);
2464 }
2465
2466 /*
2467 * Note: We explicitly do NOT connect the send udp socket. This is because
2468 * each individual packet needs to be treated independant of the others.
2469 * Connecting the socket to simplify send causes the socket to close if
2470 * certain ICMP messages come back. We specifically do NOT want this behavior.
2471 */
2472 OWPBoolean
_OWPEndpointInitHook(OWPControl cntrl,OWPTestSession tsession,OWPAcceptType * aval,OWPErrSeverity * err_ret)2473 _OWPEndpointInitHook(
2474 OWPControl cntrl,
2475 OWPTestSession tsession,
2476 OWPAcceptType *aval,
2477 OWPErrSeverity *err_ret
2478 )
2479 {
2480 OWPContext ctx = OWPGetContext(cntrl);
2481 OWPEndpoint *end_data = &tsession->endpoint;
2482 OWPEndpoint ep = tsession->endpoint;
2483 struct sigaction act;
2484 struct sigaction chldact,usr1act,usr2act,hupact,termact;
2485 struct sigaction intact,pipeact,alrmact;
2486 sigset_t sigs,osigs;
2487
2488 /*
2489 * By default, failures from here are recoverable... Set this
2490 * to OWPErrFATAL to make the connection close. And by default,
2491 * the "reason" given for Accept is failure.
2492 */
2493 *err_ret = OWPErrWARNING;
2494 *aval = OWP_CNTRL_FAILURE;
2495
2496 if(!ep){
2497 return False;
2498 }
2499
2500 /*
2501 * Initialize crypto if needed
2502 */
2503 if(ep->cntrl->mode & OWP_MODE_DOCIPHER){
2504 uint8_t iv[16];
2505 keyInstance sidkey;
2506 int r;
2507
2508 /*
2509 * Generate AES and HMAC keys needed for Test Session crypto.
2510 * (See Section 4.1 of RFC 4656.)
2511 *
2512 * initialize an aes key structure to be used for generating the
2513 * test aeskey and hmac-key. (key bytes for this is from SID)
2514 */
2515 sidkey.Nr = rijndaelKeySetupEnc(sidkey.rk,tsession->sid,
2516 sizeof(tsession->sid)*8);
2517
2518 /*
2519 * generate OWAMP test aes key bytes AES ECB mode, and initialize
2520 * an aes key structure.
2521 */
2522 rijndaelEncrypt(sidkey.rk,sidkey.Nr,ep->cntrl->aessession_key,
2523 ep->aesbytes);
2524
2525 if(ep->send){
2526 /* send side needs encryption */
2527 ep->aeskey.Nr = rijndaelKeySetupEnc(ep->aeskey.rk,ep->aesbytes,
2528 sizeof(ep->aesbytes)*8);
2529 }
2530 else{
2531 /* recv side needs decryption */
2532 ep->aeskey.Nr = rijndaelKeySetupDec(ep->aeskey.rk,ep->aesbytes,
2533 sizeof(ep->aesbytes)*8);
2534 }
2535
2536 /*
2537 * generate OWAMP test hmac key bytes AES CBC mode.
2538 */
2539 memset(iv,0,sizeof(iv));
2540 r = blockEncrypt(iv,&sidkey,cntrl->hmac_key,sizeof(cntrl->hmac_key)*8,
2541 ep->hmac_key);
2542 if(r != (sizeof(cntrl->hmac_key)*8)){
2543 OWPError(cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2544 "blockEncrypt(): Failed to create Test Session HMAC");
2545 return False;
2546 }
2547
2548 /*
2549 * Allocate an hmac_ctx for Test packets
2550 */
2551 if( !(ep->hmac_ctx = I2HMACSha1Alloc(OWPContextErrHandle(cntrl->ctx)))){
2552 return False;
2553 }
2554 }
2555
2556 if(!ep->send){
2557 ep->remoteaddr = tsession->sender;
2558 ep->localaddr = tsession->receiver;
2559 }
2560 else{
2561 ep->remoteaddr = tsession->receiver;
2562 ep->localaddr = tsession->sender;
2563 }
2564
2565 /*
2566 * call sigprocmask to block signals before the fork.
2567 * (This ensures no race condition.)
2568 * First we set the new sig_handler for the child, saving the
2569 * currently installed handlers.
2570 * Then fork.
2571 * Then reset the previous sig_handlers for the parent.
2572 * Then unblock the signals in the parent.
2573 * (This should ensure that this routine doesn't mess with what
2574 * the calling environment thinks is installed for these.)
2575 *
2576 * The Child then waits for the signals using sigsuspend, and the
2577 * newly installed handlers get called.
2578 */
2579 sigemptyset(&sigs);
2580 sigaddset(&sigs,SIGUSR1);
2581 sigaddset(&sigs,SIGUSR2);
2582 sigaddset(&sigs,SIGHUP);
2583 sigaddset(&sigs,SIGTERM);
2584 sigaddset(&sigs,SIGINT);
2585 sigaddset(&sigs,SIGALRM);
2586 sigaddset(&sigs,SIGPIPE);
2587 sigaddset(&sigs,SIGCHLD);
2588
2589 if(sigprocmask(SIG_BLOCK,&sigs,&osigs) != 0){
2590 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"sigprocmask(): %M");
2591 EndpointFree(ep,OWP_CNTRL_FAILURE);
2592 *end_data = NULL;
2593 return False;
2594 }
2595 /*
2596 * set the sig handlers for the currently blocked signals.
2597 */
2598 owp_usr1 = 0;
2599 owp_usr2 = 0;
2600 owp_int = 0;
2601 act.sa_handler = sig_catch;
2602 sigemptyset(&act.sa_mask);
2603 act.sa_flags = 0;
2604
2605 if( (sigaction(SIGUSR1,&act,&usr1act) != 0) ||
2606 (sigaction(SIGUSR2,&act,&usr2act) != 0) ||
2607 (sigaction(SIGINT,&act,&intact) != 0) ||
2608 (sigaction(SIGALRM,&act,&alrmact) != 0)){
2609 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"sigaction(): %M");
2610 EndpointFree(ep,OWP_CNTRL_FAILURE);
2611 *end_data = NULL;
2612 return False;
2613 }
2614
2615 /*
2616 * In the child, ignore PIPE, HUP, TERM.
2617 */
2618 act.sa_handler = SIG_IGN;
2619 if( (sigaction(SIGPIPE,&act,&pipeact) != 0) ||
2620 (sigaction(SIGHUP,&act,&hupact) != 0) ||
2621 (sigaction(SIGTERM,&act,&termact) != 0)){
2622 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"sigaction(): %M");
2623 EndpointFree(ep,OWP_CNTRL_FAILURE);
2624 *end_data = NULL;
2625 return False;
2626 }
2627
2628 /*
2629 * If there is currently no SIGCHLD handler:
2630 * setup an empty CHLD handler to ensure SIGCHLD is sent
2631 * to this process. (Just need the signal sent to break
2632 * us out of "select" with an EINTR when we trying to
2633 * determine if test sessions are complete.)
2634 */
2635 sigemptyset(&chldact.sa_mask);
2636 chldact.sa_handler = SIG_DFL;
2637 chldact.sa_flags = 0;
2638 /* fetch current handler */
2639 if(sigaction(SIGCHLD,NULL,&chldact) != 0){
2640 OWPError(ctx,OWPErrWARNING,OWPErrUNKNOWN,"sigaction(): %M");
2641 EndpointFree(ep,OWP_CNTRL_FAILURE);
2642 *end_data = NULL;
2643 return False;
2644 }
2645 /* if there is currently no handler - set one. */
2646 if(chldact.sa_handler == SIG_DFL){
2647 chldact.sa_handler = sig_nothing;
2648 if(sigaction(SIGCHLD,&chldact,NULL) != 0){
2649 OWPError(ctx,OWPErrWARNING,OWPErrUNKNOWN,
2650 "sigaction(DFL) failed: %M");
2651 EndpointFree(ep,OWP_CNTRL_FAILURE);
2652 *end_data = NULL;
2653 return False;
2654 }
2655 }
2656 /* now make sure SIGCHLD won't be masked. */
2657 sigdelset(&osigs,SIGCHLD);
2658
2659 ep->child = fork();
2660
2661 if(ep->child < 0){
2662 /* fork error */
2663 (void)sigprocmask(SIG_SETMASK,&osigs,NULL);
2664 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,"fork(): %M");
2665 EndpointFree(ep,OWP_CNTRL_FAILURE);
2666 *end_data = NULL;
2667 return False;
2668 }
2669
2670 if(ep->child > 0){
2671 /* parent */
2672 int childstatus;
2673
2674 /*
2675 * Reset parent's sig handlers.
2676 */
2677 if( (sigaction(SIGUSR1,&usr1act,NULL) != 0) ||
2678 (sigaction(SIGUSR2,&usr2act,NULL) != 0) ||
2679 (sigaction(SIGINT,&intact,NULL) != 0) ||
2680 (sigaction(SIGHUP,&hupact,NULL) != 0) ||
2681 (sigaction(SIGTERM,&termact,NULL) != 0) ||
2682 (sigaction(SIGPIPE,&pipeact,NULL) != 0) ||
2683 (sigaction(SIGALRM,&alrmact,NULL) != 0)){
2684 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2685 "sigaction(): %M");
2686 goto parenterr;
2687 }
2688
2689 /* reset sig_mask to the old one (-SIGCHLD) */
2690 if(sigprocmask(SIG_SETMASK,&osigs,NULL) != 0){
2691 OWPError(ctx,OWPErrWARNING,OWPErrUNKNOWN,
2692 "sigprocmask(): %M");
2693 goto parenterr;
2694 }
2695
2696
2697 EndpointClear(ep);
2698 *err_ret = OWPErrOK;
2699 *aval = OWP_CNTRL_ACCEPT;
2700 return True;
2701 parenterr:
2702 kill(ep->child,SIGINT);
2703 ep->wopts &= ~WNOHANG;
2704 while((waitpid(ep->child,&childstatus,ep->wopts) < 0)
2705 && (errno == EINTR));
2706 EndpointFree(ep,OWP_CNTRL_FAILURE);
2707 *end_data = NULL;
2708 return False;
2709 }
2710
2711 /*
2712 * We are now in the child send/recv process.
2713 */
2714
2715 /*
2716 * Create new session - do not want signals sent to the parent
2717 * process group to propagate to this process unless the parent
2718 * explicitely does it.
2719 */
2720 if((OWPBoolean)OWPContextConfigGetV(ctx,OWPDetachProcesses)
2721 && (setsid() == -1)){
2722 OWPError(ctx,OWPErrFATAL,errno,"setsid(): %M");
2723 exit(OWP_CNTRL_FAILURE);
2724 }
2725
2726 /*
2727 * Create new session - do not want signals sent to the parent
2728 * process group to propogate to this process unless the parent
2729 * explicitely does it.
2730 */
2731
2732 /*
2733 * busy loop for systems where debugger doesn't support
2734 * child follow_fork mode functionality...
2735 */
2736 #ifndef NDEBUG
2737 {
2738 void *waitfor = OWPContextConfigGetV(ctx,OWPChildWait);
2739
2740 if(waitfor){
2741 OWPError(ctx,OWPErrWARNING,OWPErrUNKNOWN,
2742 "PID=[%d] Busy-loop...",getpid());
2743 while(waitfor);
2744 }
2745 }
2746 #endif
2747
2748 /*
2749 * SIGUSR1 is StartSessions
2750 * SIGUSR2 is StopSessions
2751 * SIGINT is Terminate - making session invalid.
2752 */
2753
2754 /*
2755 * wait until signal to kick-off session.
2756 */
2757 sigemptyset(&sigs);
2758 sigaddset(&sigs,SIGPIPE);
2759 while(!owp_usr1 && !owp_usr2 && !owp_int)
2760 (void)sigsuspend(&sigs);
2761
2762 /*
2763 * got a signal - continue.
2764 */
2765 if(owp_int || owp_usr2){
2766 /* cancel the session */
2767 exit(OWP_CNTRL_REJECT);
2768 }else if(owp_usr1){
2769 /* start the session */
2770
2771 /* clear the sig mask so all sigs come through */
2772 if(sigprocmask(SIG_SETMASK,&sigs,NULL) != 0){
2773 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2774 "sigprocmask(): %M");
2775 exit(OWP_CNTRL_FAILURE);
2776 }
2777
2778 if(ep->send){
2779 run_sender(ep);
2780 }
2781 else{
2782 run_receiver(ep);
2783 }
2784 }
2785
2786 /*NOTREACHED*/
2787 OWPError(ctx,OWPErrFATAL,OWPErrUNKNOWN,
2788 "Shouldn't get to this line of code... Hmmpf.");
2789 exit(OWP_CNTRL_FAILURE);
2790 }
2791
2792 OWPBoolean
_OWPEndpointStart(OWPEndpoint ep,OWPErrSeverity * err_ret)2793 _OWPEndpointStart(
2794 OWPEndpoint ep,
2795 OWPErrSeverity *err_ret
2796 )
2797 {
2798 *err_ret = OWPErrOK;
2799
2800 if((ep->acceptval < 0) && ep->child && (kill(ep->child,SIGUSR1) == 0))
2801 return True;
2802
2803 *err_ret = OWPErrFATAL;
2804 OWPError(ep->tsession->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2805 "EndpointStart:Can't signal child #%d: %M",ep->child);
2806 return False;
2807 }
2808
2809 void
_OWPEndpointStatus(OWPEndpoint ep,OWPAcceptType * aval,OWPErrSeverity * err_ret)2810 _OWPEndpointStatus(
2811 OWPEndpoint ep,
2812 OWPAcceptType *aval, /* out */
2813 OWPErrSeverity *err_ret
2814 )
2815 {
2816 pid_t p;
2817 int childstatus;
2818
2819 *err_ret = OWPErrOK;
2820
2821 if(ep->acceptval < 0){
2822 AGAIN:
2823 p = waitpid(ep->child,&childstatus,ep->wopts);
2824 if(p < 0){
2825 if(errno == EINTR)
2826 goto AGAIN;
2827 OWPError(ep->cntrl->ctx,OWPErrWARNING,
2828 OWPErrUNKNOWN,
2829 "_OWPEndpointStatus:Can't query child #%d: %M",
2830 ep->child);
2831 ep->acceptval = OWP_CNTRL_FAILURE;
2832 *err_ret = OWPErrWARNING;
2833 }
2834 else if(p > 0){
2835 if(WIFEXITED(childstatus)){
2836 ep->acceptval =
2837 (OWPAcceptType)WEXITSTATUS(childstatus);
2838 }
2839 else{
2840 ep->acceptval = OWP_CNTRL_FAILURE;
2841 *err_ret = OWPErrWARNING;
2842 }
2843 }
2844 /*
2845 * if (p == 0) Process still running just fine. Fall through.
2846 */
2847 }
2848
2849 if(*aval == OWP_CNTRL_ACCEPT){
2850 *aval = ep->acceptval;
2851 }
2852
2853 return;
2854 }
2855
2856
2857 void
_OWPEndpointStop(OWPEndpoint ep,OWPAcceptType * aval,OWPErrSeverity * err_ret)2858 _OWPEndpointStop(
2859 OWPEndpoint ep,
2860 OWPAcceptType *aval,
2861 OWPErrSeverity *err_ret
2862 )
2863 {
2864 int sig;
2865 OWPAcceptType teststatus=OWP_CNTRL_ACCEPT;
2866
2867 if((ep->acceptval >= 0) || (ep->child == 0)){
2868 *err_ret = OWPErrOK;
2869 goto done;
2870 }
2871
2872 *err_ret = OWPErrFATAL;
2873
2874 if(*aval == OWP_CNTRL_ACCEPT){
2875 sig = SIGUSR2;
2876 }
2877 else{
2878 sig = SIGINT;
2879 }
2880
2881 /*
2882 * If child already exited, kill will come back with ESRCH
2883 */
2884 if((kill(ep->child,sig) != 0) && (errno != ESRCH))
2885 goto error;
2886
2887 /*
2888 * Remove the WNOHANG bit. We need to wait until the exit status
2889 * is available.
2890 * (Should we add a timer to break out? No - not that paranoid yet.)
2891 */
2892 ep->wopts &= ~WNOHANG;
2893 _OWPEndpointStatus(ep,&teststatus,err_ret);
2894 if(teststatus >= 0)
2895 goto done;
2896
2897 error:
2898 OWPError(ep->cntrl->ctx,OWPErrFATAL,OWPErrUNKNOWN,
2899 "EndpointStop:Can't signal child #%d: %M",ep->child);
2900 done:
2901 /*
2902 * If accept state was good upon calling this function, but there
2903 * was an error stopping this session - report the problem up.
2904 */
2905 if(*aval == OWP_CNTRL_ACCEPT){
2906 *aval = ep->acceptval;
2907 }
2908
2909 return;
2910 }
2911
2912 extern void
_OWPEndpointFree(OWPEndpoint ep,OWPAcceptType * aval,OWPErrSeverity * err_ret)2913 _OWPEndpointFree(
2914 OWPEndpoint ep,
2915 OWPAcceptType *aval,
2916 OWPErrSeverity *err_ret
2917 )
2918 {
2919 _OWPEndpointStop(ep,aval,err_ret);
2920
2921 ep->tsession->endpoint = NULL;
2922 EndpointFree(ep,*aval);
2923
2924 return;
2925 }
2926