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