1 /* WIREFILTER (C) 2005 Renzo Davoli
2 * Licensed under the GPLv2
3 * Modified by Ludovico Gardenghi 2005
4 * Modified by Renzo Davoli, Luca Bigliardi 2007
5 * Gauss normal distribution/blinking support, requested and parlty implemented
6 * by Luca Saiu and Jean-Vincent Loddo (Marionnet project)
7 * Gilbert model for packet loss requested by Leandro Galvao.
9 * This filter can be used for testing network protcols.
10 * It is possible to loose, delay or reorder packets.
11 * Options can be set on command line or interactively with a remote interface
12 * on a unix socket (see unixterm).
30 #include <sys/types.h>
32 #include <sys/socket.h>
37 #include <vdecommon.h>
38 #include <libvdeplug.h>
40 #if defined(VDE_DARWIN) || defined(VDE_FREEBSD)
42 # if defined HAVE_SYSLIMITS_H
43 # include <syslimits.h>
44 # elif defined HAVE_SYS_SYSLIMITS_H
45 # include <sys/syslimits.h>
47 # error "No syslimits.h found"
53 static int alternate_stdin
;
54 static int alternate_stdout
;
55 #define NPFD NPIPES+NPIPES+MAXCONN+1
56 struct pollfd pfd
[NPFD
];
63 #define ALGO_UNIFORM 0
64 #define ALGO_GAUSS_NORMAL 1
65 static char charalgo
[]="UN";
72 static inline double max_wirevalue(struct wirevalue
*val
)
74 return (val
->value
+ val
->plus
);
77 static inline double min_wirevalue(struct wirevalue
*val
)
79 return (val
->value
- val
->plus
);
82 struct wirevalue loss
[2];
83 struct wirevalue lostburst
[2];
84 struct wirevalue delay
[2];
85 struct wirevalue ddup
[2];
86 struct wirevalue band
[2];
87 struct wirevalue speed
[2];
88 struct wirevalue speed
[2];
89 struct wirevalue capacity
[2];
90 struct wirevalue noise
[2];
91 struct wirevalue mtu
[2];
92 /*for the Gilber model */
94 #define FAULTY_BURST 1
95 char loss_status
[2]; /* Gilbert model Markov chain status */
96 struct timeval nextband
[2];
97 struct timeval nextspeed
[2];
99 int ndirs
; //1 mono directional, 2 bi directional filter (always 2 with -v)
100 int delay_bufsize
[2]; //total size of delayed packets
101 char *vdepath
[2]; //path of the directly connected switched (via vde_plug)
102 VDECONN
*vdeplug
[2]; //vde_plug connections (if NULL stdin/stdout)
103 int daemonize
; // daemon mode
104 static char *pidfile
= NULL
;
105 static char pidfile_path
[PATH_MAX
];
107 static int blinksock
;
108 static struct sockaddr_un blinksun
;
109 static char *blinkmsg
;
110 static char blinkidlen
;
114 #define MGMTMODEARG 129
115 #define DAEMONIZEARG 130
116 #define PIDFILEARG 131
117 #define LOGSOCKETARG 132
123 static void initrand()
126 gettimeofday(&v
,NULL
);
127 srand48(v
.tv_sec
^ v
.tv_usec
^ getpid());
130 /*more than 98% inside the bell */
131 #define SIGMA (1.0/3.0)
132 static double compute_wirevalue(struct wirevalue
*wv
)
138 return wv
->value
+wv
->plus
*((drand48()*2.0)-1.0);
139 case ALGO_GAUSS_NORMAL
:
147 return wv
->value
+wv
->plus
* SIGMA
* x
* sqrt ( (-2 * log(r2
)) /r2
);
154 void printlog(int priority
, const char *format
, ...)
158 va_start (arg
, format
);
161 vsyslog(priority
,format
,arg
);
163 fprintf(stderr
,"%s: ",progname
);
164 vfprintf(stderr
,format
,arg
);
165 fprintf(stderr
,"\n");
170 static void read_wirevalue(char *s
,struct wirevalue
*wv
)
176 char algo
=ALGO_UNIFORM
;
178 while ((s
[n
] == ' ' || s
[n
] == '\n' || s
[n
] == '\t') && n
>0)
191 algo
=ALGO_GAUSS_NORMAL
;
212 if ((n
=sscanf(s
,"%lf+%lf",&v
,&vplus
)) > 0) {
213 wv
[LR
].value
=wv
[RL
].value
=v
*mult
;
214 wv
[LR
].plus
=wv
[RL
].plus
=vplus
*mult
;
215 wv
[LR
].alg
=wv
[RL
].alg
=algo
;
216 } else if ((n
=sscanf(s
,"LR%lf+%lf",&v
,&vplus
)) > 0) {
218 wv
[LR
].plus
=vplus
*mult
;
220 } else if ((n
=sscanf(s
,"RL%lf+%lf",&v
,&vplus
)) > 0) {
222 wv
[RL
].plus
=vplus
*mult
;
228 unsigned long long when
;
235 struct packpq sentinel
={0,0,NULL
,0};
237 unsigned long long maxwhen
;
246 gettimeofday(&v
,NULL
);
247 deltat
=pqh
[1]->when
-(v
.tv_sec
*1000000+v
.tv_usec
);
248 return (deltat
>0)?(int)(deltat
/1000):0;
253 static inline int outpacket(int dir
,const unsigned char *buf
,int size
)
256 snprintf(blinkmsg
+blinkidlen
,20,"%s %d\n",
257 (ndirs
==2)?((dir
==0)?"LR":"RL"):"--",
259 sendto(blinksock
,blinkmsg
,strlen(blinkmsg
+blinkidlen
)+blinkidlen
,0,
260 (struct sockaddr
*)&blinksun
, sizeof(blinksun
));
263 return vde_send(vdeplug
[1-dir
],buf
+2,size
-2,0);
265 return write(outfd
[dir
],buf
,size
);
268 int writepacket(int dir
,const unsigned char *buf
,int size
)
271 if (max_wirevalue(noise
+dir
) > 0) {
272 double noiseval
=compute_wirevalue(noise
+dir
);
274 while ((drand48()*8*MEGA
) < (size
-2)*8*noiseval
)
277 unsigned char noisedpacket
[BUFSIZE
];
278 memcpy(noisedpacket
,buf
,size
);
280 int flippedbit
=(drand48()*size
*8);
281 noisedpacket
[(flippedbit
>> 3) + 2] ^= 1<<(flippedbit
& 0x7);
284 return outpacket(dir
,noisedpacket
,size
);
286 return outpacket(dir
,buf
,size
);
288 return outpacket(dir
,buf
,size
);
291 /* packet queues are priority queues implemented on a heap.
292 * enqueue time = dequeue time = O(log n) max&mean
295 static void packet_dequeue()
298 gettimeofday(&v
,NULL
);
299 unsigned long long now
=v
.tv_sec
*1000000+v
.tv_usec
;
300 while (npq
>0 && pqh
[1]->when
<= now
) {
301 struct packpq
*old
=pqh
[npq
--];
303 delay_bufsize
[pqh
[1]->dir
] -= pqh
[1]->size
;
304 writepacket(pqh
[1]->dir
,pqh
[1]->buf
,pqh
[1]->size
);
310 if (j
<npq
&& pqh
[j
]->when
> pqh
[j
+1]->when
) j
++;
311 if (old
->when
<= pqh
[j
]->when
) {
321 static void packet_enqueue(int dir
,const unsigned char *buf
,int size
,int delms
)
326 /* when bandwidth is limited, packets exceeding capacity are discarded */
327 if (max_wirevalue(capacity
+dir
) > 0) {
328 double capval
=compute_wirevalue(capacity
+dir
);
329 if ((delay_bufsize
[dir
]+size
) > capval
)
334 struct packpq
*new=malloc(sizeof(struct packpq
));
336 printlog(LOG_WARNING
,"%s: malloc elem %s",progname
,strerror(errno
));
339 gettimeofday(&v
,NULL
);
340 new->when
=v
.tv_sec
* 1000000 + v
.tv_usec
+ delms
* 1000;
341 if (new->when
> maxwhen
) maxwhen
=new->when
;
342 if (!nofifo
&& new->when
< maxwhen
) new->when
=maxwhen
;
344 new->buf
=malloc(size
);
345 if (new->buf
==NULL
) {
346 printlog(LOG_WARNING
,"%s: malloc elem buf %s",progname
,strerror(errno
));
349 memcpy(new->buf
,buf
,size
);
351 delay_bufsize
[dir
]+=size
;
353 pqh
=malloc(PQCHUNK
*sizeof(struct packpq
*));
355 printlog(LOG_WARNING
,"%s: malloc %s",progname
,strerror(errno
));
358 pqh
[0]=&sentinel
; maxpq
=PQCHUNK
;
361 pqh
=realloc(pqh
,(maxpq
=maxpq
+PQCHUNK
) * sizeof(struct packpq
*));
363 printlog(LOG_WARNING
,"%s: malloc %s",progname
,strerror(errno
));
368 while (new->when
< pqh
[k
>>1]->when
) {
376 void handle_packet(int dir
,const unsigned char *buf
,int size
)
379 /* if the packet is incosistent with the MTU of the line just drop it */
380 if (min_wirevalue(mtu
+dir
) > 0 && size
> min_wirevalue(mtu
+dir
))
384 /* Total packet loss */
385 if (min_wirevalue(loss
+dir
) >= 100.0)
387 /* probabilistic loss */
388 if (max_wirevalue(lostburst
+dir
) > 0) {
390 double losval
=compute_wirevalue(loss
+dir
)/100;
391 double burstlen
=compute_wirevalue(lostburst
+dir
);
392 double alpha
=losval
/ (burstlen
*(1-losval
));
393 double beta
=1.0 / burstlen
;
394 switch (loss_status
[dir
]) {
396 if (drand48() < alpha
) loss_status
[dir
]=FAULTY_BURST
;
399 if (drand48() < beta
) loss_status
[dir
]=OK_BURST
;
402 if (loss_status
[dir
] != OK_BURST
)
404 } else if (max_wirevalue(loss
+dir
) > 0) {
405 /* standard non bursty model */
406 double losval
=compute_wirevalue(loss
+dir
)/100;
407 if (drand48() < losval
)
412 /* times is the number of dup packets */
414 if (max_wirevalue(ddup
+dir
) > 0) {
415 double dupval
=compute_wirevalue(ddup
+dir
)/100;
416 while (drand48() < dupval
)
423 /* speed limit, if packets arrive too fast, delay the sender */
424 if (max_wirevalue(speed
+dir
) > 0) {
425 double speedval
=compute_wirevalue(speed
+dir
);
426 if (speedval
<=0) return;
428 unsigned int commtime
=((unsigned)size
)*1000000/((unsigned int)speedval
);
430 gettimeofday(&tv
,NULL
);
431 banddelay
=commtime
/1000;
432 if (timercmp(&tv
,&nextspeed
[dir
], > ))
434 nextspeed
[dir
].tv_usec
+= commtime
;
435 nextspeed
[dir
].tv_sec
+= nextspeed
[dir
].tv_usec
/ 1000000;
436 nextspeed
[dir
].tv_usec
%= 1000000;
441 /* band, when band overflows, delay just the delivery */
442 if (max_wirevalue(band
+dir
) > 0) {
443 double bandval
=compute_wirevalue(band
+dir
);
444 if (bandval
<=0) return;
446 unsigned int commtime
=((unsigned)size
)*1000000/((unsigned int)bandval
);
448 gettimeofday(&tv
,NULL
);
449 if (timercmp(&tv
,&nextband
[dir
], > )) {
451 banddelay
=commtime
/1000;
453 timersub(&nextband
[dir
],&tv
,&tv
);
454 banddelay
=tv
.tv_sec
*1000 + (tv
.tv_usec
+ commtime
)/1000;
456 nextband
[dir
].tv_usec
+= commtime
;
457 nextband
[dir
].tv_sec
+= nextband
[dir
].tv_usec
/ 1000000;
458 nextband
[dir
].tv_usec
%= 1000000;
465 if (banddelay
>= 0) {
466 if (banddelay
> 0 || max_wirevalue(delay
+dir
) > 0) {
467 double delval
=compute_wirevalue(delay
+dir
);
468 delval
=(delval
>= 0)?delval
+banddelay
:banddelay
;
470 packet_enqueue(dir
,buf
,size
,(int) delval
);
472 writepacket(dir
,buf
,size
);
474 writepacket(dir
,buf
,size
);
480 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
482 static void splitpacket(const unsigned char *buf
,int size
,int dir
)
484 static unsigned char fragment
[BUFSIZE
][2];
485 static unsigned char *fragp
[2];
486 static unsigned int rnx
[2],remaining
[2];
488 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",progname,rnx[dir],remaining[dir],size);
491 register int amount
=MIN(remaining
[dir
],size
);
492 //fprintf(stderr,"%s: fragment amount %d\n",progname,amount);
493 memcpy(fragp
[dir
],buf
,amount
);
494 remaining
[dir
]-=amount
;
498 if (remaining
[dir
]==0) {
499 //fprintf(stderr,"%s: delivered defrag %d\n",progname,rnx[dir]);
500 handle_packet(dir
,fragment
[dir
],rnx
[dir
]+2);
505 rnx
[dir
]=(buf
[0]<<8)+buf
[1];
506 //fprintf(stderr,"%s: packet %d size %d %x %x dir %d\n",progname,rnx[dir],size-2,buf[0],buf[1],dir);
508 printlog(LOG_WARNING
,"%s: Packet length error size %d rnx %d",progname
,size
,rnx
[dir
]);
512 if (rnx
[dir
]+2 > size
) {
513 //fprintf(stderr,"%s: begin defrag %d\n",progname,rnx[dir]);
514 fragp
[dir
]=fragment
[dir
];
515 memcpy(fragp
[dir
],buf
,size
);
516 remaining
[dir
]=rnx
[dir
]+2-size
;
520 handle_packet(dir
,buf
,rnx
[dir
]+2);
528 static void packet_in(int dir
)
530 unsigned char buf
[BUFSIZE
];
533 n
=vde_recv(vdeplug
[dir
],buf
+2,BUFSIZE
-2,0);
536 handle_packet(dir
,buf
,n
+2);
538 n
=read(pfd
[dir
].fd
,buf
,BUFSIZE
);
541 splitpacket(buf
,n
,dir
);
545 static int check_open_fifos_n_plugs(struct pollfd
*pfd
,int *outfd
,char *vdepath
[],VDECONN
*vdeplug
[])
548 struct stat stfd
[NPIPES
];
551 env_in
=getenv("ALTERNATE_STDIN");
552 env_out
=getenv("ALTERNATE_STDOUT");
554 alternate_stdin
=atoi(env_in
);
556 alternate_stdout
=atoi(env_out
);
557 if (vdepath
[0]) { // -v selected
558 if (strcmp(vdepath
[0],"-") != 0) {
559 if((vdeplug
[LR
]=vde_open(vdepath
[0],"vde_crosscable",NULL
))==NULL
){
560 fprintf(stderr
,"vdeplug %s: %s\n",vdepath
[0],strerror(errno
));
563 pfd
[0].fd
=vde_datafd(vdeplug
[LR
]);
564 pfd
[0].events
=POLLIN
| POLLHUP
;
566 if (strcmp(vdepath
[1],"-") != 0) {
567 if((vdeplug
[RL
]=vde_open(vdepath
[1],"vde_crosscable",NULL
))==NULL
){
568 fprintf(stderr
,"vdeplug %s: %s\n",vdepath
[1],strerror(errno
));
571 pfd
[1].fd
=vde_datafd(vdeplug
[RL
]);
572 pfd
[1].events
=POLLIN
| POLLHUP
;
576 if (vdeplug
[LR
] == NULL
|| vdeplug
[RL
] == NULL
) {
577 if (fstat(STDIN_FILENO
,&stfd
[STDIN_FILENO
]) < 0) {
578 fprintf(stderr
,"%s: Error on stdin: %s\n",progname
,strerror(errno
));
581 if (fstat(STDOUT_FILENO
,&stfd
[STDOUT_FILENO
]) < 0) {
582 fprintf(stderr
,"%s: Error on stdout: %s\n",progname
,strerror(errno
));
585 if (!S_ISFIFO(stfd
[STDIN_FILENO
].st_mode
)) {
586 fprintf(stderr
,"%s: Error on stdin: %s\n",progname
,"it is not a pipe");
589 if (!S_ISFIFO(stfd
[STDOUT_FILENO
].st_mode
)) {
590 fprintf(stderr
,"%s: Error on stdin: %s\n",progname
,"it is not a pipe");
593 if (vdeplug
[RL
] != NULL
) { /* -v -:xxx */
594 pfd
[0].fd
=STDIN_FILENO
;
595 pfd
[0].events
=POLLIN
| POLLHUP
;
596 outfd
[1]=STDOUT_FILENO
;
597 } else if (vdeplug
[LR
] != NULL
) { /* -v xxx:- */
598 pfd
[1].fd
=STDIN_FILENO
;
599 pfd
[1].events
=POLLIN
| POLLHUP
;
600 outfd
[0]=STDOUT_FILENO
;
601 } else if (env_in
== NULL
|| fstat(alternate_stdin
,&stfd
[0]) < 0) {
603 pfd
[0].fd
=STDIN_FILENO
;
604 pfd
[0].events
=POLLIN
| POLLHUP
;
605 outfd
[0]=STDOUT_FILENO
;
607 if (fstat(outfd
[1],&stfd
[1]) < 0) {
608 fprintf(stderr
,"%s: Error on secondary out: %s\n",progname
,strerror(errno
));
611 if (!S_ISFIFO(stfd
[0].st_mode
)) {
612 fprintf(stderr
,"%s: Error on secondary in: %s\n",progname
,"it is not a pipe");
615 if (!S_ISFIFO(stfd
[1].st_mode
)) {
616 fprintf(stderr
,"%s: Error on secondary out: %s\n",progname
,"it is not a pipe");
620 pfd
[LR
].fd
=STDIN_FILENO
;
621 pfd
[LR
].events
=POLLIN
| POLLHUP
;
622 outfd
[LR
]=alternate_stdout
;
623 pfd
[RL
].fd
=alternate_stdin
;
624 pfd
[RL
].events
=POLLIN
| POLLHUP
;
625 outfd
[RL
]=STDOUT_FILENO
;
631 static void save_pidfile()
633 if(pidfile
[0] != '/')
634 strncat(pidfile_path
, pidfile
, PATH_MAX
- strlen(pidfile_path
));
636 strcpy(pidfile_path
, pidfile
);
638 int fd
= open(pidfile_path
,
639 O_WRONLY
| O_CREAT
| O_EXCL
,
640 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
644 printlog(LOG_ERR
, "Error in pidfile creation: %s", strerror(errno
));
648 if((f
= fdopen(fd
, "w")) == NULL
) {
649 printlog(LOG_ERR
, "Error in FILE* construction: %s", strerror(errno
));
653 if(fprintf(f
, "%ld\n", (long int)getpid()) <= 0) {
654 printlog(LOG_ERR
, "Error in writing pidfile");
661 static void cleanup(void)
663 if((pidfile
!= NULL
) && unlink(pidfile_path
) < 0) {
664 printlog(LOG_WARNING
,"Couldn't remove pidfile '%s': %s", pidfile
, strerror(errno
));
667 vde_close(vdeplug
[LR
]);
669 vde_close(vdeplug
[RL
]);
674 static void sig_handler(int sig
)
676 /*fprintf(stderr,"Caught signal %d, cleaning up and exiting", sig);*/
678 signal(sig
, SIG_DFL
);
682 static void setsighandlers()
684 /* setting signal handlers.
685 * * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
686 * * * ignores all the others signals which could cause termination. */
687 struct { int sig
; const char *name
; int ignore
; } signals
[] = {
688 { SIGHUP
, "SIGHUP", 0 },
689 { SIGINT
, "SIGINT", 0 },
690 { SIGPIPE
, "SIGPIPE", 1 },
691 { SIGALRM
, "SIGALRM", 1 },
692 { SIGTERM
, "SIGTERM", 0 },
693 { SIGUSR1
, "SIGUSR1", 1 },
694 { SIGUSR2
, "SIGUSR2", 1 },
695 { SIGPROF
, "SIGPROF", 1 },
696 { SIGVTALRM
, "SIGVTALRM", 1 },
698 { SIGPOLL
, "SIGPOLL", 1 },
700 { SIGSTKFLT
, "SIGSTKFLT", 1 },
702 { SIGIO
, "SIGIO", 1 },
703 { SIGPWR
, "SIGPWR", 1 },
705 { SIGUNUSED
, "SIGUNUSED", 1 },
709 { SIGXCPU
, "SIGXCPU", 1 },
710 { SIGXFSZ
, "SIGXFSZ", 1 },
716 for(i
= 0; signals
[i
].sig
!= 0; i
++)
717 if(signal(signals
[i
].sig
,
718 signals
[i
].ignore
? SIG_IGN
: sig_handler
) < 0)
719 fprintf(stderr
,"%s: Setting handler for %s: %s", progname
, signals
[i
].name
,
723 static int openmgmt(char *mgmt
)
726 struct sockaddr_un sun
;
729 if((mgmtconnfd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0){
730 fprintf(stderr
,"%s: mgmt socket: %s",progname
,strerror(errno
));
733 if(setsockopt(mgmtconnfd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
735 fprintf(stderr
,"%s: mgmt setsockopt: %s",progname
,strerror(errno
));
738 if(fcntl(mgmtconnfd
, F_SETFL
, O_NONBLOCK
) < 0){
739 fprintf(stderr
,"%s: Setting O_NONBLOCK on mgmt fd: %s",progname
,strerror(errno
));
742 sun
.sun_family
= PF_UNIX
;
743 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s",mgmt
);
744 if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
745 fprintf(stderr
,"%s: mgmt bind %s",progname
,strerror(errno
));
748 chmod(sun
.sun_path
,mgmtmode
);
749 if(listen(mgmtconnfd
, 15) < 0){
750 fprintf(stderr
,"%s: mgmt listen: %s",progname
,strerror(errno
));
756 static char header
[]="\nVDE wirefilter V.%s\n(C) R.Davoli 2005,2006 - GPLv2\n";
757 static char prompt
[]="\nVDEwf$ ";
758 static int newmgmtconn(int fd
,struct pollfd
*pfd
,int nfds
)
763 struct sockaddr addr
;
764 new = accept(fd
, &addr
, &len
);
766 printlog(LOG_WARNING
,"%s: mgmt accept %s",progname
,strerror(errno
));
770 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
771 write(new,buf
,strlen(buf
));
772 write(new,prompt
,strlen(prompt
));
774 pfd
[nfds
].events
=POLLIN
| POLLHUP
;
777 printlog(LOG_WARNING
,"%s: too many mgmt connections",progname
);
783 static void printoutc(int fd
, const char *format
, ...)
786 char outbuf
[MAXCMD
+1];
788 va_start (arg
, format
);
789 vsnprintf(outbuf
,MAXCMD
,format
,arg
);
791 write(fd
,outbuf
,strlen(outbuf
));
794 static int setdelay(int fd
,char *s
)
796 read_wirevalue(s
,delay
);
800 static int setloss(int fd
,char *s
)
802 read_wirevalue(s
,loss
);
806 static int setlostburst(int fd
,char *s
)
808 read_wirevalue(s
,lostburst
);
809 if (max_wirevalue(lostburst
+LR
) == 0)
810 loss_status
[LR
]=OK_BURST
;
811 if (max_wirevalue(lostburst
+RL
) == 0)
812 loss_status
[RL
]=OK_BURST
;
816 static int setddup(int fd
,char *s
)
818 read_wirevalue(s
,ddup
);
822 static int setband(int fd
,char *s
)
824 read_wirevalue(s
,band
);
828 static int setnoise(int fd
,char *s
)
830 read_wirevalue(s
,noise
);
834 static int setmtu(int fd
,char *s
)
836 read_wirevalue(s
,mtu
);
840 static int setspeed(int fd
,char *s
)
842 read_wirevalue(s
,speed
);
846 static int setcapacity(int fd
,char *s
)
848 read_wirevalue(s
,capacity
);
852 static int setfifo(int fd
,char *s
)
862 static int logout(int fd
,char *s
)
867 static int doshutdown(int fd
,char *s
)
873 static int help(int fd
,char *s
)
875 printoutc(fd
, "help: print a summary of mgmt commands");
876 printoutc(fd
, "showinfo: show status and parameter values");
877 printoutc(fd
, "loss: set loss percentage");
878 printoutc(fd
, "lostburst: mean length of lost packet bursts");
879 printoutc(fd
, "delay: set delay ms");
880 printoutc(fd
, "dup: set dup packet percentage");
881 printoutc(fd
, "bandwidth: set channel bandwidth bytes/sec");
882 printoutc(fd
, "speed: set interface speed bytes/sec");
883 printoutc(fd
, "noise: set noise factor bits/Mbyte");
884 printoutc(fd
, "mtu: set channel MTU (bytes)");
885 printoutc(fd
, "capacity: set channel capacity (bytes)");
886 printoutc(fd
, "fifo: set channel fifoness");
887 printoutc(fd
, "shutdown: shut the channel down");
888 printoutc(fd
, "logout: log out from this mgmt session");
892 #define CHARALGO(X) (charalgo[(int)(X)])
893 #define WIREVALUE_FIELDS(X) (X)->value,(X)->plus,(charalgo[(int)((X)->alg)])
894 static int showinfo(int fd
,char *s
)
896 printoutc(fd
, "WireFilter: %sdirectional",(ndirs
==2)?"bi":"mono");
898 printoutc(fd
, "Loss L->R %g+%g%c R->L %g+%g%c",
899 WIREVALUE_FIELDS(loss
+LR
),
900 WIREVALUE_FIELDS(loss
+RL
));
901 printoutc(fd
, "Lburst L->R %g+%g%c R->L %g+%g%c",
902 WIREVALUE_FIELDS(lostburst
+LR
),
903 WIREVALUE_FIELDS(lostburst
+RL
));
904 printoutc(fd
, "Delay L->R %g+%g%c R->L %g+%g%c",
905 WIREVALUE_FIELDS(delay
+LR
),
906 WIREVALUE_FIELDS(delay
+RL
));
907 printoutc(fd
, "Dup L->R %g+%g%c R->L %g+%g%c",
908 WIREVALUE_FIELDS(ddup
+LR
),
909 WIREVALUE_FIELDS(ddup
+RL
));
910 printoutc(fd
, "Bandw L->R %g+%g%c R->L %g+%g%c",
911 WIREVALUE_FIELDS(band
+LR
),
912 WIREVALUE_FIELDS(band
+RL
));
913 printoutc(fd
, "Speed L->R %g+%g%c R->L %g+%g%c",
914 WIREVALUE_FIELDS(speed
+LR
),
915 WIREVALUE_FIELDS(speed
+RL
));
916 printoutc(fd
, "Noise L->R %g+%g%c R->L %g+%g%c",
917 WIREVALUE_FIELDS(noise
+LR
),
918 WIREVALUE_FIELDS(noise
+RL
));
919 printoutc(fd
, "MTU L->R %g R->L %g ",
920 min_wirevalue(mtu
+LR
),
921 min_wirevalue(mtu
+RL
));
922 printoutc(fd
, "Cap. L->R %g+%g%c R->L %g+%g%c",
923 WIREVALUE_FIELDS(capacity
+LR
),
924 WIREVALUE_FIELDS(capacity
+RL
));
925 printoutc(fd
, "Current Delay Queue size: L->R %d R->L %d ",delay_bufsize
[LR
],delay_bufsize
[RL
]);
927 printoutc(fd
, "Loss %g+%g%c",
928 WIREVALUE_FIELDS(loss
));
929 printoutc(fd
, "Lburst %g+%g%c",
930 WIREVALUE_FIELDS(lostburst
));
931 printoutc(fd
, "Delay %g+%g%c",
932 WIREVALUE_FIELDS(delay
));
933 printoutc(fd
, "Dup %g+%g%c",
934 WIREVALUE_FIELDS(ddup
));
935 printoutc(fd
, "Bandw %g+%g%c",
936 WIREVALUE_FIELDS(band
));
937 printoutc(fd
, "Speed %g+%g%c",
938 WIREVALUE_FIELDS(speed
));
939 printoutc(fd
, "Noise %g+%g%c",
940 WIREVALUE_FIELDS(noise
));
941 printoutc(fd
, "MTU %g", min_wirevalue(mtu
));
942 printoutc(fd
, "Cap. %g+%g%c",
943 WIREVALUE_FIELDS(capacity
));
944 printoutc(fd
, "Current Delay Queue size: %d",delay_bufsize
[0]);
946 printoutc(fd
,"Fifoness %s",(nofifo
== 0)?"TRUE":"FALSE");
947 printoutc(fd
,"Waiting packets in delay queues %d",npq
);
949 blinkmsg
[(int)blinkidlen
]=0;
950 printoutc(fd
,"Blink socket: %s",blinksun
.sun_path
);
951 printoutc(fd
,"Blink id: %s",blinkmsg
);
957 static struct comlist
{
959 int (*fun
)(int fd
,char *arg
);
962 {"help", help
, WITHFD
},
963 {"showinfo",showinfo
, WITHFD
},
964 {"delay",setdelay
, 0},
966 {"lostburst",setlostburst
, 0},
968 {"bandwidth",setband
, 0},
970 {"speed",setspeed
, 0},
971 {"capacity",setcapacity
, 0},
972 {"noise",setnoise
, 0},
975 {"logout",logout
, 0},
976 {"shutdown",doshutdown
, 0}
979 #define NCL sizeof(commandlist)/sizeof(struct comlist)
981 static int handle_cmd(int fd
,char *inbuf
)
985 while (*inbuf
== ' ' || *inbuf
== '\t' || *inbuf
== '\n') inbuf
++;
986 if (*inbuf
!= '\0' && *inbuf
!= '#') {
988 && strncmp(commandlist
[i
].tag
,inbuf
,strlen(commandlist
[i
].tag
))!=0;
993 inbuf
+= strlen(commandlist
[i
].tag
);
994 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
995 if (commandlist
[i
].type
& WITHFD
)
996 printoutc(fd
,"0000 DATA END WITH '.'");
997 rv
=commandlist
[i
].fun(fd
,inbuf
);
998 if (commandlist
[i
].type
& WITHFD
)
1001 printoutc(fd
,"1%03d %s",rv
,strerror(rv
));
1007 static int mgmtcommand(int fd
)
1012 n
= read(fd
, buf
, MAXCMD
);
1014 printlog(LOG_WARNING
,"%s: read from mgmt %s",progname
,strerror(errno
));
1020 if (fd
==STDIN_FILENO
)
1021 outfd
=STDOUT_FILENO
;
1023 rv
=handle_cmd(outfd
,buf
);
1025 write(outfd
,prompt
,strlen(prompt
));
1030 static int delmgmtconn(int i
,struct pollfd
*pfd
,int nfds
)
1034 if (pfd
[i
].fd
== 0) /* close stdin implies exit */
1036 memmove(pfd
+i
,pfd
+i
+1,sizeof (struct pollfd
) * (nfds
-i
-1));
1044 fprintf(stderr
,"Usage: %s OPTIONS\n"
1046 "\t--loss|-l loss_percentage\n"
1047 "\t--lostburst|-L lost_packet_burst_len\n"
1048 "\t--delay|-d delay_ms\n"
1049 "\t--dup|-D dup_percentage\n"
1050 "\t--band|-b bandwidth(bytes/s)\n"
1051 "\t--speed|-s interface_speed(bytes/s)\n"
1052 "\t--capacity|-c delay_channel_capacity\n"
1053 "\t--noise|-n noise_bits/megabye\n"
1054 "\t--mtu|-m mtu_size\n"
1056 "\t--mgmt|-M management_socket\n"
1057 "\t--mgmtmode management_permission(octal)\n"
1058 "\t--vde-plug plug1:plug2 | -v plug1:plug2\n"
1060 "\t--pidfile pidfile\n"
1061 "\t--blink blinksocket\n"
1062 "\t--blinkid blink_id_string\n"
1067 int main(int argc
,char *argv
[])
1073 int consoleindex
=-1;
1074 static struct option long_options
[] = {
1075 {"help",0 , 0, 'h'},
1076 {"loss", 1, 0, 'l'},
1077 {"lostburst", 1, 0, 'L'},
1078 {"delay",1 , 0, 'd'},
1080 {"band",1 , 0, 'b'},
1081 {"speed",1 , 0, 's'},
1082 {"capacity",1 , 0, 'c'},
1083 {"noise",1 , 0, 'n'},
1085 {"nofifo",0 , 0, 'N'},
1086 {"mgmt", 1, 0, 'M'},
1087 {"mgmtmode", 1, 0, MGMTMODEARG
},
1088 {"vde-plug",1,0,'v'},
1089 {"daemon",0 , 0, DAEMONIZEARG
},
1090 {"pidfile", 1, 0, PIDFILEARG
},
1091 {"blink",1,0,LOGSOCKETARG
},
1092 {"blinkid",1,0,LOGIDARG
}
1094 progname
=basename(argv
[0]);
1101 c
= GETOPT_LONG (argc
, argv
, "hnl:d:M:D:m:b:s:c:v:L:",
1102 long_options
, &option_index
);
1110 read_wirevalue(optarg
,delay
);
1113 read_wirevalue(optarg
,loss
);
1116 read_wirevalue(optarg
,lostburst
);
1119 read_wirevalue(optarg
,ddup
);
1122 read_wirevalue(optarg
,band
);
1125 read_wirevalue(optarg
,mtu
);
1128 read_wirevalue(optarg
,noise
);
1131 read_wirevalue(optarg
,speed
);
1134 read_wirevalue(optarg
,capacity
);
1137 mgmt
=strdup(optarg
);
1145 vdepath
[LR
]=strdup(optarg
);
1146 colon
=index(vdepath
[LR
],':');
1149 vdepath
[RL
]=colon
+1;
1151 fprintf(stderr
,"Bad vde_plugs specification.\n");
1156 sscanf(optarg
,"%o",&mgmtmode
);
1162 pidfile
=strdup(optarg
);
1165 blinksun
.sun_family
= PF_UNIX
;
1166 snprintf(blinksun
.sun_path
,sizeof(blinksun
.sun_path
),"%s",optarg
);
1169 if (blinkmsg
) free(blinkmsg
);
1170 blinkidlen
=strlen(optarg
)+1;
1171 asprintf(&blinkmsg
,"%s 12345678901234567890",optarg
);
1181 if (blinksun
.sun_path
[0] != 0) {
1182 blinksock
=socket(AF_UNIX
, SOCK_DGRAM
, 0);
1183 if (blinkmsg
==NULL
) {
1185 asprintf(&blinkmsg
,"%06d 12345678901234567890",getpid());
1190 * monodir: 0 input LR, 1 mgmtctl, >1 mgmt open conn (mgmtindex==ndirs==1)
1191 * bidir on streams: 0 input LR, 1 input RL, 2 mgmtctl, >2 mgmt open conn (mgmtindex==ndirs==2)
1192 * vdeplug xx:xx : 0 input LR, 1 input RL, 2&3 ctlfd, 4 mgmtctl, > 4 mgmt open conn (mgmtindex>ndirs==2) 5 is console
1193 * vdeplug xx:xx : 0 input LR, 1 input RL, 2&3 ctlfd, 4 console (if not -M)
1194 * vdeplug -:xx : 0 input LR(stdin), 1 input RL, 2 ctlfd, 3 mgmtctl, > 3 mgmt open conn (mgmtindex>ndirs==2)
1195 * vdeplug xx:- : 0 input LR, 1 input RL(stdin), 2 ctlfd, 3 mgmtctl, > 3 mgmt open conn (mgmtindex>ndirs==2)
1198 ndirs
=check_open_fifos_n_plugs(pfd
,outfd
,vdepath
,vdeplug
);
1205 pfd
[npfd
].fd
=vde_ctlfd(vdeplug
[LR
]);
1206 pfd
[npfd
].events
=POLLIN
| POLLHUP
;
1210 pfd
[npfd
].fd
=vde_ctlfd(vdeplug
[RL
]);
1211 pfd
[npfd
].events
=POLLIN
| POLLHUP
;
1216 int mgmtfd
=openmgmt(mgmt
);
1218 pfd
[mgmtindex
].fd
=mgmtfd
;
1219 pfd
[mgmtindex
].events
=POLLIN
| POLLHUP
;
1224 openlog(progname
, LOG_PID
, 0);
1226 } else if (vdeplug
[LR
] && vdeplug
[RL
]) { // console mode
1228 pfd
[npfd
].fd
=STDIN_FILENO
;
1229 pfd
[npfd
].events
=POLLIN
| POLLHUP
;
1233 /* saves current path in pidfile_path, because otherwise with daemonize() we
1235 if(getcwd(pidfile_path
, PATH_MAX
-1) == NULL
) {
1236 printlog(LOG_ERR
, "getcwd: %s", strerror(errno
));
1239 strcat(pidfile_path
, "/");
1240 if (daemonize
&& daemon(0, 0)) {
1241 printlog(LOG_ERR
,"daemon: %s",strerror(errno
));
1245 /* once here, we're sure we're the true process which will continue as a
1246 * server: save PID file if needed */
1247 if(pidfile
) save_pidfile();
1250 printlog(LOG_INFO
,"%s: bidirectional vdeplug filter L=%s R=%s starting...",progname
,
1251 (*vdepath
[LR
])?vdepath
[LR
]:"DEFAULT_SWITCH",
1252 (*vdepath
[RL
])?vdepath
[RL
]:"DEFAULT_SWITCH");
1254 printlog(LOG_INFO
,"%s: bidirectional filter starting...",progname
);
1256 printlog(LOG_INFO
,"%s: monodirectional filter starting...",progname
);
1261 pfd
[0].events
|= POLLIN
;
1262 if (speed
[LR
].value
> 0) {
1265 gettimeofday(&tv
,NULL
);
1266 if (timercmp(&tv
, &nextspeed
[LR
], <)) {
1267 timersub(&nextspeed
[LR
],&tv
,&tv
);
1268 speeddelay
=tv
.tv_sec
*1000 + tv
.tv_usec
/1000;
1269 if (speeddelay
> 0) {
1270 pfd
[0].events
&= ~POLLIN
;
1271 if (speeddelay
< delay
|| delay
< 0) delay
=speeddelay
;
1276 pfd
[1].events
|= POLLIN
;
1277 if (speed
[RL
].value
> 0) {
1280 if (timercmp(&tv
, &nextspeed
[RL
], <)) {
1281 gettimeofday(&tv
,NULL
);
1282 timersub(&nextspeed
[RL
],&tv
,&tv
);
1283 speeddelay
=tv
.tv_sec
*1000 + tv
.tv_usec
/1000;
1284 if (speeddelay
> 0) {
1285 pfd
[1].events
&= ~POLLIN
;
1286 if (speeddelay
< delay
|| delay
< 0) delay
=speeddelay
;
1291 n
=poll(pfd
,npfd
,delay
);
1292 if (pfd
[0].revents
& POLLHUP
|| (ndirs
>1 && pfd
[1].revents
& POLLHUP
))
1294 if (pfd
[0].revents
& POLLIN
) {
1297 if (ndirs
>1 && pfd
[1].revents
& POLLIN
) {
1300 if (n
>0) { // if there are already events to handle (performance: packet switching first)
1301 int mgmtfdstart
=consoleindex
;
1302 if (mgmtindex
>= 0) mgmtfdstart
=mgmtindex
+1;
1303 if (mgmtfdstart
>= 0 && npfd
> mgmtfdstart
) {
1305 for (i
=mgmtfdstart
;i
<npfd
;i
++) {
1306 if (pfd
[i
].revents
& POLLIN
&& mgmtcommand(pfd
[i
].fd
) < 0)
1307 pfd
[i
].revents
|= POLLHUP
;
1308 if (pfd
[i
].revents
) n
--;
1310 for (i
=mgmtfdstart
;i
<npfd
;i
++) {
1311 if (pfd
[i
].revents
& POLLHUP
)
1312 npfd
=delmgmtconn(i
,pfd
,npfd
);
1315 if (mgmtindex
>= 0) {
1316 if (pfd
[mgmtindex
].revents
!= 0) {
1317 npfd
=newmgmtconn(pfd
[mgmtindex
].fd
,pfd
,npfd
);
1321 /* if (n>0) // if there are already pending events, it means that a ctlfd has hunged up