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
6 * This filter can be used for testing network protcols.
7 * It is possible to loose, delay or reorder packets.
8 * Options can be set on command line or interactively with a remote interface
9 * on a unix socket (see unixterm).
26 #include <sys/types.h>
28 #include <sys/socket.h>
33 #include <vdecommon.h>
34 #include <libvdeplug.h>
36 #if defined(VDE_DARWIN) || defined(VDE_FREEBSD)
38 # if defined HAVE_SYSLIMITS_H
39 # include <syslimits.h>
40 # elif defined HAVE_SYS_SYSLIMITS_H
41 # include <sys/syslimits.h>
43 # error "No syslimits.h found"
49 static int alternate_stdin
;
50 static int alternate_stdout
;
51 #define NPFD NPIPES+NPIPES+MAXCONN+1
52 struct pollfd pfd
[NPFD
];
59 double loss
[2],lossplus
[2];
60 double lostburst
[2],lostburstplus
[2];
61 double delay
[2],delayplus
[2];
62 double ddup
[2],ddupplus
[2];
63 double band
[2],bandplus
[2];
64 double speed
[2],speedplus
[2];
65 double capacity
[2],capacityplus
[2];
66 double noise
[2],noiseplus
[2];
67 double mtu
[2],mtuplus
[2];
68 #define ALGO_UNIFORM 0
69 #define ALGO_GAUSS_NORMAL 1
70 static char charalgo
[]="UN";
71 char lossalg
[2],lostburstalg
[2],delayalg
[2],ddupalg
[2];
72 char bandalg
[2],speedalg
[2],capacityalg
[2],noisealg
[2],mtualg
[2];
73 /*for the Gilber model */
75 #define FAULTY_BURST 1
77 struct timeval nextband
[2];
78 struct timeval nextspeed
[2];
80 int ndirs
; //1 mono directional, 2 bi directional filter (always 2 with -v)
81 int delay_bufsize
[2]; //total size of delayed packets
82 char *vdepath
[2]; //path of the directly connected switched (via vde_plug)
83 VDECONN
*vdeplug
[2]; //vde_plug connections (if NULL stdin/stdout)
84 int daemonize
; // daemon mode
85 static char *pidfile
= NULL
;
86 static char pidfile_path
[PATH_MAX
];
91 #define MGMTMODEARG 129
92 #define DAEMONIZEARG 130
93 #define PIDFILEARG 131
98 static void initrand()
101 gettimeofday(&v
,NULL
);
102 srand48(v
.tv_sec
^ v
.tv_usec
^ getpid());
105 /*more than 98% inside the bell */
106 #define SIGMA (1.0/3.0)
107 static double offset_distr(char algorithm
,double range
)
113 return range
*((drand48()*2.0)-1.0);
114 case ALGO_GAUSS_NORMAL
:
122 return range
* SIGMA
* x
* sqrt ( (-2 * log(r2
)) /r2
);
129 void printlog(int priority
, const char *format
, ...)
133 va_start (arg
, format
);
136 vsyslog(priority
,format
,arg
);
138 fprintf(stderr
,"%s: ",progname
);
139 vfprintf(stderr
,format
,arg
);
140 fprintf(stderr
,"\n");
145 static void readdualvalue(char *s
,double *val
,double *valplus
,char *algorithm
)
151 char algo
=ALGO_UNIFORM
;
153 while ((s
[n
] == ' ' || s
[n
] == '\n' || s
[n
] == '\t') && n
>0)
166 algo
=ALGO_GAUSS_NORMAL
;
187 if ((n
=sscanf(s
,"%lf+%lf",&v
,&vplus
)) > 0) {
188 val
[LR
]=val
[RL
]=v
*mult
;
189 valplus
[LR
]=valplus
[RL
]=vplus
*mult
;
190 algorithm
[LR
]=algorithm
[RL
]=algo
;
191 } else if ((n
=sscanf(s
,"LR%lf+%lf",&v
,&vplus
)) > 0) {
193 valplus
[LR
]=vplus
*mult
;
195 } else if ((n
=sscanf(s
,"RL%lf+%lf",&v
,&vplus
)) > 0) {
197 valplus
[RL
]=vplus
*mult
;
203 unsigned long long when
;
210 struct packpq sentinel
={0,0,NULL
,0};
212 unsigned long long maxwhen
;
221 gettimeofday(&v
,NULL
);
222 deltat
=pqh
[1]->when
-(v
.tv_sec
*1000000+v
.tv_usec
);
223 return (deltat
>0)?(int)(deltat
/1000):0;
228 static inline int outpacket(int dir
,const unsigned char *buf
,int size
)
231 return vde_send(vdeplug
[1-dir
],buf
+2,size
-2,0);
233 return write(outfd
[dir
],buf
,size
);
236 int writepacket(int dir
,const unsigned char *buf
,int size
)
239 if (noise
[dir
]+noiseplus
[dir
] > 0) {
240 double noiseval
=noise
[dir
];
242 if (noiseplus
[dir
]) noiseval
+=offset_distr(noisealg
[dir
],noiseplus
[dir
]);
243 while ((drand48()*8*MEGA
) < (size
-2)*8*noiseval
)
246 unsigned char noisedpacket
[BUFSIZE
];
247 memcpy(noisedpacket
,buf
,size
);
249 int flippedbit
=(drand48()*size
*8);
250 noisedpacket
[(flippedbit
>> 3) + 2] ^= 1<<(flippedbit
& 0x7);
253 return outpacket(dir
,noisedpacket
,size
);
255 return outpacket(dir
,buf
,size
);
257 return outpacket(dir
,buf
,size
);
260 /* packet queues are priority queues implemented on a heap.
261 * enqueue time = dequeue time = O(log n) max&mean
264 static void packet_dequeue()
267 gettimeofday(&v
,NULL
);
268 unsigned long long now
=v
.tv_sec
*1000000+v
.tv_usec
;
269 while (npq
>0 && pqh
[1]->when
<= now
) {
270 struct packpq
*old
=pqh
[npq
--];
272 delay_bufsize
[pqh
[1]->dir
] -= pqh
[1]->size
;
273 writepacket(pqh
[1]->dir
,pqh
[1]->buf
,pqh
[1]->size
);
279 if (j
<npq
&& pqh
[j
]->when
> pqh
[j
+1]->when
) j
++;
280 if (old
->when
<= pqh
[j
]->when
) {
290 static void packet_enqueue(int dir
,const unsigned char *buf
,int size
,int delms
)
295 /* when bandwidth is limited, packets exceeding capacity are discarded */
296 if (capacity
[dir
]+capacityplus
[dir
] > 0) {
297 double capval
=capacity
[dir
];
298 if (capacityplus
[dir
])
299 capval
+=offset_distr(capacityalg
[dir
],capacityplus
[dir
]);
300 if ((delay_bufsize
[dir
]+size
) > capval
)
305 struct packpq
*new=malloc(sizeof(struct packpq
));
307 printlog(LOG_WARNING
,"%s: malloc elem %s",progname
,strerror(errno
));
310 gettimeofday(&v
,NULL
);
311 new->when
=v
.tv_sec
* 1000000 + v
.tv_usec
+ delms
* 1000;
312 if (new->when
> maxwhen
) maxwhen
=new->when
;
313 if (!nofifo
&& new->when
< maxwhen
) new->when
=maxwhen
;
315 new->buf
=malloc(size
);
316 if (new->buf
==NULL
) {
317 printlog(LOG_WARNING
,"%s: malloc elem buf %s",progname
,strerror(errno
));
320 memcpy(new->buf
,buf
,size
);
322 delay_bufsize
[dir
]+=size
;
324 pqh
=malloc(PQCHUNK
*sizeof(struct packpq
*));
326 printlog(LOG_WARNING
,"%s: malloc %s",progname
,strerror(errno
));
329 pqh
[0]=&sentinel
; maxpq
=PQCHUNK
;
332 pqh
=realloc(pqh
,(maxpq
=maxpq
+PQCHUNK
) * sizeof(struct packpq
*));
334 printlog(LOG_WARNING
,"%s: malloc %s",progname
,strerror(errno
));
339 while (new->when
< pqh
[k
>>1]->when
) {
347 void handle_packet(int dir
,const unsigned char *buf
,int size
)
350 /* if the packet is incosistent with the MTU of the line just drop it */
351 if (mtu
[dir
] > 0 && size
> mtu
[dir
])
355 /* Total packet loss */
356 if (loss
[dir
]-lossplus
[dir
] >= 100.0)
358 /* probabilistic loss */
359 if (lostburst
[dir
]+ lostburstplus
[dir
] > 0) {
361 double losval
=(loss
[dir
]+offset_distr(lossalg
[dir
],lossplus
[dir
]))/100;
362 double burstlen
=(lostburst
[dir
]+offset_distr(lostburstalg
[dir
],lostburstplus
[dir
]));
363 double alpha
=losval
/ (burstlen
*(1-losval
));
364 double beta
=1.0 / burstlen
;
365 switch (loss_status
[dir
]) {
367 if (drand48() < alpha
) loss_status
[dir
]=FAULTY_BURST
;
370 if (drand48() < beta
) loss_status
[dir
]=OK_BURST
;
373 if (loss_status
[dir
] != OK_BURST
)
375 } else if (loss
[dir
]+lossplus
[dir
] > 0) {
376 /* standard non bursty model */
377 double losval
=(loss
[dir
]+offset_distr(lossalg
[dir
],lossplus
[dir
]))/100;
378 if (drand48() < losval
)
383 /* times is the number of dup packets */
385 if (ddup
[dir
]+ddupplus
[dir
] > 0) {
386 double dupval
=(ddup
[dir
]+offset_distr(ddupalg
[dir
],ddupplus
[dir
]))/100;
387 while (drand48() < dupval
)
394 /* speed limit, if packets arrive too fast, delay the sender */
395 if (speed
[dir
]+speedplus
[dir
] > 0) {
396 double speedval
=speed
[dir
];
397 if (speedplus
[dir
]) {
398 speedval
+=offset_distr(speedalg
[dir
],speedplus
[dir
]);
399 if (speedval
<=0) return;
402 unsigned int commtime
=((unsigned)size
)*1000000/((unsigned int)speedval
);
404 gettimeofday(&tv
,NULL
);
405 banddelay
=commtime
/1000;
406 if (timercmp(&tv
,&nextspeed
[dir
], > ))
408 nextspeed
[dir
].tv_usec
+= commtime
;
409 nextspeed
[dir
].tv_sec
+= nextspeed
[dir
].tv_usec
/ 1000000;
410 nextspeed
[dir
].tv_usec
%= 1000000;
415 /* band, when band overflows, delay just the delivery */
416 if (band
[dir
]+bandplus
[dir
] > 0) {
417 double bandval
=band
[dir
];
419 bandval
+=offset_distr(bandalg
[dir
],bandplus
[dir
]);
420 if (bandval
<=0) return;
423 unsigned int commtime
=((unsigned)size
)*1000000/((unsigned int)bandval
);
425 gettimeofday(&tv
,NULL
);
426 if (timercmp(&tv
,&nextband
[dir
], > )) {
428 banddelay
=commtime
/1000;
430 timersub(&nextband
[dir
],&tv
,&tv
);
431 banddelay
=tv
.tv_sec
*1000 + (tv
.tv_usec
+ commtime
)/1000;
433 nextband
[dir
].tv_usec
+= commtime
;
434 nextband
[dir
].tv_sec
+= nextband
[dir
].tv_usec
/ 1000000;
435 nextband
[dir
].tv_usec
%= 1000000;
442 if (banddelay
>= 0) {
443 if (banddelay
> 0 || delay
[dir
]+delayplus
[dir
] > 0) {
444 double delval
=(delay
[dir
]+offset_distr(delayalg
[dir
],delayplus
[dir
]));
445 delval
=(delval
>= 0)?delval
+banddelay
:banddelay
;
447 packet_enqueue(dir
,buf
,size
,(int) delval
);
449 writepacket(dir
,buf
,size
);
451 writepacket(dir
,buf
,size
);
457 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
459 static void splitpacket(const unsigned char *buf
,int size
,int dir
)
461 static unsigned char fragment
[BUFSIZE
][2];
462 static unsigned char *fragp
[2];
463 static unsigned int rnx
[2],remaining
[2];
465 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",progname,rnx[dir],remaining[dir],size);
468 register int amount
=MIN(remaining
[dir
],size
);
469 //fprintf(stderr,"%s: fragment amount %d\n",progname,amount);
470 memcpy(fragp
[dir
],buf
,amount
);
471 remaining
[dir
]-=amount
;
475 if (remaining
[dir
]==0) {
476 //fprintf(stderr,"%s: delivered defrag %d\n",progname,rnx[dir]);
477 handle_packet(dir
,fragment
[dir
],rnx
[dir
]+2);
482 rnx
[dir
]=(buf
[0]<<8)+buf
[1];
483 //fprintf(stderr,"%s: packet %d size %d %x %x dir %d\n",progname,rnx[dir],size-2,buf[0],buf[1],dir);
485 printlog(LOG_WARNING
,"%s: Packet length error size %d rnx %d",progname
,size
,rnx
[dir
]);
489 if (rnx
[dir
]+2 > size
) {
490 //fprintf(stderr,"%s: begin defrag %d\n",progname,rnx[dir]);
491 fragp
[dir
]=fragment
[dir
];
492 memcpy(fragp
[dir
],buf
,size
);
493 remaining
[dir
]=rnx
[dir
]+2-size
;
497 handle_packet(dir
,buf
,rnx
[dir
]+2);
505 static void packet_in(int dir
)
507 unsigned char buf
[BUFSIZE
];
510 n
=vde_recv(vdeplug
[dir
],buf
+2,BUFSIZE
-2,0);
513 handle_packet(dir
,buf
,n
+2);
515 n
=read(pfd
[dir
].fd
,buf
,BUFSIZE
);
518 splitpacket(buf
,n
,dir
);
522 static int check_open_fifos_n_plugs(struct pollfd
*pfd
,int *outfd
,char *vdepath
[],VDECONN
*vdeplug
[])
525 struct stat stfd
[NPIPES
];
528 env_in
=getenv("ALTERNATE_STDIN");
529 env_out
=getenv("ALTERNATE_STDOUT");
531 alternate_stdin
=atoi(env_in
);
533 alternate_stdout
=atoi(env_out
);
534 if (vdepath
[0]) { // -v selected
535 if (strcmp(vdepath
[0],"-") != 0) {
536 if((vdeplug
[LR
]=vde_open(vdepath
[0],"vde_crosscable",NULL
))==NULL
){
537 fprintf(stderr
,"vdeplug %s: %s\n",vdepath
[0],strerror(errno
));
540 pfd
[0].fd
=vde_datafd(vdeplug
[LR
]);
541 pfd
[0].events
=POLLIN
| POLLHUP
;
543 if (strcmp(vdepath
[1],"-") != 0) {
544 if((vdeplug
[RL
]=vde_open(vdepath
[1],"vde_crosscable",NULL
))==NULL
){
545 fprintf(stderr
,"vdeplug %s: %s\n",vdepath
[1],strerror(errno
));
548 pfd
[1].fd
=vde_datafd(vdeplug
[RL
]);
549 pfd
[1].events
=POLLIN
| POLLHUP
;
553 if (vdeplug
[LR
] == NULL
|| vdeplug
[RL
] == NULL
) {
554 if (fstat(STDIN_FILENO
,&stfd
[STDIN_FILENO
]) < 0) {
555 fprintf(stderr
,"%s: Error on stdin: %s\n",progname
,strerror(errno
));
558 if (fstat(STDOUT_FILENO
,&stfd
[STDOUT_FILENO
]) < 0) {
559 fprintf(stderr
,"%s: Error on stdout: %s\n",progname
,strerror(errno
));
562 if (!S_ISFIFO(stfd
[STDIN_FILENO
].st_mode
)) {
563 fprintf(stderr
,"%s: Error on stdin: %s\n",progname
,"it is not a pipe");
566 if (!S_ISFIFO(stfd
[STDOUT_FILENO
].st_mode
)) {
567 fprintf(stderr
,"%s: Error on stdin: %s\n",progname
,"it is not a pipe");
570 if (vdeplug
[RL
] != NULL
) { /* -v -:xxx */
571 pfd
[0].fd
=STDIN_FILENO
;
572 pfd
[0].events
=POLLIN
| POLLHUP
;
573 outfd
[1]=STDOUT_FILENO
;
574 } else if (vdeplug
[LR
] != NULL
) { /* -v xxx:- */
575 pfd
[1].fd
=STDIN_FILENO
;
576 pfd
[1].events
=POLLIN
| POLLHUP
;
577 outfd
[0]=STDOUT_FILENO
;
578 } else if (env_in
== NULL
|| fstat(alternate_stdin
,&stfd
[0]) < 0) {
580 pfd
[0].fd
=STDIN_FILENO
;
581 pfd
[0].events
=POLLIN
| POLLHUP
;
582 outfd
[0]=STDOUT_FILENO
;
584 if (fstat(outfd
[1],&stfd
[1]) < 0) {
585 fprintf(stderr
,"%s: Error on secondary out: %s\n",progname
,strerror(errno
));
588 if (!S_ISFIFO(stfd
[0].st_mode
)) {
589 fprintf(stderr
,"%s: Error on secondary in: %s\n",progname
,"it is not a pipe");
592 if (!S_ISFIFO(stfd
[1].st_mode
)) {
593 fprintf(stderr
,"%s: Error on secondary out: %s\n",progname
,"it is not a pipe");
597 pfd
[LR
].fd
=STDIN_FILENO
;
598 pfd
[LR
].events
=POLLIN
| POLLHUP
;
599 outfd
[LR
]=alternate_stdout
;
600 pfd
[RL
].fd
=alternate_stdin
;
601 pfd
[RL
].events
=POLLIN
| POLLHUP
;
602 outfd
[RL
]=STDOUT_FILENO
;
608 static void save_pidfile()
610 if(pidfile
[0] != '/')
611 strncat(pidfile_path
, pidfile
, PATH_MAX
- strlen(pidfile_path
));
613 strcpy(pidfile_path
, pidfile
);
615 int fd
= open(pidfile_path
,
616 O_WRONLY
| O_CREAT
| O_EXCL
,
617 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
621 printlog(LOG_ERR
, "Error in pidfile creation: %s", strerror(errno
));
625 if((f
= fdopen(fd
, "w")) == NULL
) {
626 printlog(LOG_ERR
, "Error in FILE* construction: %s", strerror(errno
));
630 if(fprintf(f
, "%ld\n", (long int)getpid()) <= 0) {
631 printlog(LOG_ERR
, "Error in writing pidfile");
638 static void cleanup(void)
640 if((pidfile
!= NULL
) && unlink(pidfile_path
) < 0) {
641 printlog(LOG_WARNING
,"Couldn't remove pidfile '%s': %s", pidfile
, strerror(errno
));
644 vde_close(vdeplug
[LR
]);
646 vde_close(vdeplug
[RL
]);
651 static void sig_handler(int sig
)
653 /*fprintf(stderr,"Caught signal %d, cleaning up and exiting", sig);*/
655 signal(sig
, SIG_DFL
);
659 static void setsighandlers()
661 /* setting signal handlers.
662 * * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
663 * * * ignores all the others signals which could cause termination. */
664 struct { int sig
; const char *name
; int ignore
; } signals
[] = {
665 { SIGHUP
, "SIGHUP", 0 },
666 { SIGINT
, "SIGINT", 0 },
667 { SIGPIPE
, "SIGPIPE", 1 },
668 { SIGALRM
, "SIGALRM", 1 },
669 { SIGTERM
, "SIGTERM", 0 },
670 { SIGUSR1
, "SIGUSR1", 1 },
671 { SIGUSR2
, "SIGUSR2", 1 },
672 { SIGPROF
, "SIGPROF", 1 },
673 { SIGVTALRM
, "SIGVTALRM", 1 },
675 { SIGPOLL
, "SIGPOLL", 1 },
677 { SIGSTKFLT
, "SIGSTKFLT", 1 },
679 { SIGIO
, "SIGIO", 1 },
680 { SIGPWR
, "SIGPWR", 1 },
682 { SIGUNUSED
, "SIGUNUSED", 1 },
686 { SIGXCPU
, "SIGXCPU", 1 },
687 { SIGXFSZ
, "SIGXFSZ", 1 },
693 for(i
= 0; signals
[i
].sig
!= 0; i
++)
694 if(signal(signals
[i
].sig
,
695 signals
[i
].ignore
? SIG_IGN
: sig_handler
) < 0)
696 fprintf(stderr
,"%s: Setting handler for %s: %s", progname
, signals
[i
].name
,
700 static int openmgmt(char *mgmt
)
703 struct sockaddr_un sun
;
706 if((mgmtconnfd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0){
707 fprintf(stderr
,"%s: mgmt socket: %s",progname
,strerror(errno
));
710 if(setsockopt(mgmtconnfd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
712 fprintf(stderr
,"%s: mgmt setsockopt: %s",progname
,strerror(errno
));
715 if(fcntl(mgmtconnfd
, F_SETFL
, O_NONBLOCK
) < 0){
716 fprintf(stderr
,"%s: Setting O_NONBLOCK on mgmt fd: %s",progname
,strerror(errno
));
719 sun
.sun_family
= PF_UNIX
;
720 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s",mgmt
);
721 if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
722 fprintf(stderr
,"%s: mgmt bind %s",progname
,strerror(errno
));
725 chmod(sun
.sun_path
,mgmtmode
);
726 if(listen(mgmtconnfd
, 15) < 0){
727 fprintf(stderr
,"%s: mgmt listen: %s",progname
,strerror(errno
));
733 static char header
[]="\nVDE wirefilter V.%s\n(C) R.Davoli 2005,2006 - GPLv2\n";
734 static char prompt
[]="\nVDEwf$ ";
735 static int newmgmtconn(int fd
,struct pollfd
*pfd
,int nfds
)
740 struct sockaddr addr
;
741 new = accept(fd
, &addr
, &len
);
743 printlog(LOG_WARNING
,"%s: mgmt accept %s",progname
,strerror(errno
));
747 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
748 write(new,buf
,strlen(buf
));
749 write(new,prompt
,strlen(prompt
));
751 pfd
[nfds
].events
=POLLIN
| POLLHUP
;
754 printlog(LOG_WARNING
,"%s: too many mgmt connections",progname
);
760 static void printoutc(int fd
, const char *format
, ...)
763 char outbuf
[MAXCMD
+1];
765 va_start (arg
, format
);
766 vsnprintf(outbuf
,MAXCMD
,format
,arg
);
768 write(fd
,outbuf
,strlen(outbuf
));
771 static int setdelay(int fd
,char *s
)
773 readdualvalue(s
,delay
,delayplus
,delayalg
);
777 static int setloss(int fd
,char *s
)
779 readdualvalue(s
,loss
,lossplus
,lossalg
);
783 static int setlostburst(int fd
,char *s
)
785 readdualvalue(s
,lostburst
,lostburstplus
,lostburstalg
);
786 if (lostburst
[LR
]+lostburstplus
[LR
] == 0)
787 loss_status
[LR
]=OK_BURST
;
788 if (lostburst
[RL
]+lostburstplus
[RL
] == 0)
789 loss_status
[RL
]=OK_BURST
;
793 static int setddup(int fd
,char *s
)
795 readdualvalue(s
,ddup
,ddupplus
,ddupalg
);
799 static int setband(int fd
,char *s
)
801 readdualvalue(s
,band
,bandplus
,bandalg
);
805 static int setnoise(int fd
,char *s
)
807 readdualvalue(s
,noise
,noiseplus
,noisealg
);
811 static int setmtu(int fd
,char *s
)
813 readdualvalue(s
,mtu
,mtuplus
,mtualg
);
817 static int setspeed(int fd
,char *s
)
819 readdualvalue(s
,speed
,speedplus
,speedalg
);
823 static int setcapacity(int fd
,char *s
)
825 readdualvalue(s
,capacity
,capacityplus
,capacityalg
);
829 static int setfifo(int fd
,char *s
)
839 static int logout(int fd
,char *s
)
844 static int doshutdown(int fd
,char *s
)
850 static int help(int fd
,char *s
)
852 printoutc(fd
, "help: print a summary of mgmt commands");
853 printoutc(fd
, "showinfo: show status and parameter values");
854 printoutc(fd
, "loss: set loss percentage");
855 printoutc(fd
, "lostburst: mean length of lost packet bursts");
856 printoutc(fd
, "delay: set delay ms");
857 printoutc(fd
, "dup: set dup packet percentage");
858 printoutc(fd
, "bandwidth: set channel bandwidth bytes/sec");
859 printoutc(fd
, "speed: set interface speed bytes/sec");
860 printoutc(fd
, "noise: set noise factor bits/Mbyte");
861 printoutc(fd
, "mtu: set channel MTU (bytes)");
862 printoutc(fd
, "capacity: set channel capacity (bytes)");
863 printoutc(fd
, "fifo: set channel fifoness");
864 printoutc(fd
, "shutdown: shut the channel down");
865 printoutc(fd
, "logout: log out from this mgmt session");
869 #define CHARALGO(X) (charalgo[(int)(X)])
870 static int showinfo(int fd
,char *s
)
872 printoutc(fd
, "WireFilter: %sdirectional",(ndirs
==2)?"bi":"mono");
874 printoutc(fd
, "Loss L->R %g+%g%c R->L %g+%g%c",
875 loss
[LR
],lossplus
[LR
],CHARALGO(lossalg
[LR
]),
876 loss
[RL
],lossplus
[RL
],CHARALGO(lossalg
[RL
]));
877 printoutc(fd
, "Lburst L->R %g+%g%c R->L %g+%g%c",
878 lostburst
[LR
],lostburstplus
[LR
],CHARALGO(lostburstalg
[LR
]),
879 lostburst
[RL
],lostburstplus
[RL
],CHARALGO(lostburstalg
[RL
]));
880 printoutc(fd
, "Delay L->R %g+%g%c R->L %g+%g%c",
881 delay
[LR
],delayplus
[LR
],CHARALGO(delayalg
[LR
]),
882 delay
[RL
],delayplus
[RL
],CHARALGO(delayalg
[RL
]));
883 printoutc(fd
, "Dup L->R %g+%g%c R->L %g+%g%c",
884 ddup
[LR
],ddupplus
[LR
],CHARALGO(ddupalg
[LR
]),
885 ddup
[RL
],ddupplus
[RL
],CHARALGO(ddupalg
[RL
]));
886 printoutc(fd
, "Bandw L->R %g+%g%c R->L %g+%g%c",
887 band
[LR
],bandplus
[LR
],CHARALGO(bandalg
[LR
]),
888 band
[RL
],bandplus
[RL
],CHARALGO(bandalg
[RL
]));
889 printoutc(fd
, "Speed L->R %g+%g%c R->L %g+%g%c",
890 speed
[LR
],speedplus
[LR
],CHARALGO(speedalg
[LR
]),
891 speed
[RL
],speedplus
[RL
],CHARALGO(speedalg
[RL
]));
892 printoutc(fd
, "Noise L->R %g+%g%c R->L %g+%g%c",
893 noise
[LR
],noiseplus
[LR
],CHARALGO(noisealg
[LR
]),
894 noise
[RL
],noiseplus
[RL
],CHARALGO(noisealg
[RL
]));
895 printoutc(fd
, "MTU L->R %g R->L %g ",mtu
[LR
],mtu
[RL
]);
896 printoutc(fd
, "Cap. L->R %g+%g%c R->L %g+%g%c",
897 capacity
[LR
],capacityplus
[LR
],CHARALGO(capacityalg
[LR
]),
898 capacity
[RL
],capacityplus
[RL
],CHARALGO(capacityalg
[RL
]));
899 printoutc(fd
, "Current Delay Queue size: L->R %d R->L %d ",delay_bufsize
[LR
],delay_bufsize
[RL
]);
901 printoutc(fd
, "Loss %g+%g%c",loss
[0],lossplus
[0]);
902 printoutc(fd
, "Lburst %g+%g%c",lostburst
[0],lostburstplus
[0]);
903 printoutc(fd
, "Delay %g+%g%c",delay
[0],delayplus
[0]);
904 printoutc(fd
, "Dup %g+%g%c",ddup
[0],ddupplus
[0]);
905 printoutc(fd
, "Bandw %g+%g%c",band
[0],bandplus
[0]);
906 printoutc(fd
, "Speed %g+%g%c",speed
[0],speedplus
[0]);
907 printoutc(fd
, "Noise %g+%g%c",noise
[0],noiseplus
[0]);
908 printoutc(fd
, "MTU %g",mtu
[0]);
909 printoutc(fd
, "Cap. %g+%g%c",capacity
[0],capacityplus
[0]);
910 printoutc(fd
, "Current Delay Queue size: %d",delay_bufsize
[0]);
912 printoutc(fd
,"Fifoness %s",(nofifo
== 0)?"TRUE":"FALSE");
913 printoutc(fd
,"Waiting packets in delay queues %d",npq
);
918 static struct comlist
{
920 int (*fun
)(int fd
,char *arg
);
923 {"help", help
, WITHFD
},
924 {"showinfo",showinfo
, WITHFD
},
925 {"delay",setdelay
, 0},
927 {"lostburst",setlostburst
, 0},
929 {"bandwidth",setband
, 0},
931 {"speed",setspeed
, 0},
932 {"capacity",setcapacity
, 0},
933 {"noise",setnoise
, 0},
936 {"logout",logout
, 0},
937 {"shutdown",doshutdown
, 0}
940 #define NCL sizeof(commandlist)/sizeof(struct comlist)
942 static int handle_cmd(int fd
,char *inbuf
)
946 while (*inbuf
== ' ' || *inbuf
== '\t' || *inbuf
== '\n') inbuf
++;
947 if (*inbuf
!= '\0' && *inbuf
!= '#') {
949 && strncmp(commandlist
[i
].tag
,inbuf
,strlen(commandlist
[i
].tag
))!=0;
954 inbuf
+= strlen(commandlist
[i
].tag
);
955 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
956 if (commandlist
[i
].type
& WITHFD
)
957 printoutc(fd
,"0000 DATA END WITH '.'");
958 rv
=commandlist
[i
].fun(fd
,inbuf
);
959 if (commandlist
[i
].type
& WITHFD
)
962 printoutc(fd
,"1%03d %s",rv
,strerror(rv
));
968 static int mgmtcommand(int fd
)
973 n
= read(fd
, buf
, MAXCMD
);
975 printlog(LOG_WARNING
,"%s: read from mgmt %s",progname
,strerror(errno
));
981 if (fd
==STDIN_FILENO
)
984 rv
=handle_cmd(outfd
,buf
);
986 write(outfd
,prompt
,strlen(prompt
));
991 static int delmgmtconn(int i
,struct pollfd
*pfd
,int nfds
)
995 if (pfd
[i
].fd
== 0) /* close stdin implies exit */
997 memmove(pfd
+i
,pfd
+i
+1,sizeof (struct pollfd
) * (nfds
-i
-1));
1005 fprintf(stderr
,"Usage: %s OPTIONS\n"
1007 "\t--loss|-l loss_percentage\n"
1008 "\t--lostburst|-L lost_packet_burst_len\n"
1009 "\t--delay|-d delay_ms\n"
1010 "\t--dup|-D dup_percentage\n"
1011 "\t--band|-b bandwidth(bytes/s)\n"
1012 "\t--speed|-s interface_speed(bytes/s)\n"
1013 "\t--capacity|-c delay_channel_capacity\n"
1014 "\t--noise|-n noise_bits/megabye\n"
1015 "\t--mtu|-m mtu_size\n"
1017 "\t--mgmt|-M management_socket\n"
1018 "\t--mgmtmode management_permission(octal)\n"
1019 "\t--vde-plug plug1:plug2 | -v plug1:plug2\n"
1021 "\t--pidfile pidfile\n"
1026 int main(int argc
,char *argv
[])
1032 int consoleindex
=-1;
1033 static struct option long_options
[] = {
1034 {"help",0 , 0, 'h'},
1035 {"loss", 1, 0, 'l'},
1036 {"lostburst", 1, 0, 'L'},
1037 {"delay",1 , 0, 'd'},
1039 {"band",1 , 0, 'b'},
1040 {"speed",1 , 0, 's'},
1041 {"capacity",1 , 0, 'c'},
1042 {"noise",1 , 0, 'n'},
1044 {"nofifo",0 , 0, 'N'},
1045 {"mgmt", 1, 0, 'M'},
1046 {"mgmtmode", 1, 0, MGMTMODEARG
},
1047 {"vde-plug",1,0,'v'},
1048 {"daemon",0 , 0, DAEMONIZEARG
},
1049 {"pidfile", 1, 0, PIDFILEARG
}
1051 progname
=basename(argv
[0]);
1059 c
= GETOPT_LONG (argc
, argv
, "hnl:d:M:D:m:b:s:c:v:L:",
1060 long_options
, &option_index
);
1068 readdualvalue(optarg
,delay
,delayplus
,delayalg
);
1071 readdualvalue(optarg
,loss
,lossplus
,lossalg
);
1074 readdualvalue(optarg
,lostburst
,lostburstplus
,lostburstalg
);
1077 readdualvalue(optarg
,ddup
,ddupplus
,ddupalg
);
1080 readdualvalue(optarg
,band
,bandplus
,bandalg
);
1083 readdualvalue(optarg
,mtu
,mtuplus
,mtualg
);
1086 readdualvalue(optarg
,noise
,noiseplus
,noisealg
);
1089 readdualvalue(optarg
,speed
,speedplus
,speedalg
);
1092 readdualvalue(optarg
,capacity
,capacityplus
,capacityalg
);
1095 mgmt
=strdup(optarg
);
1103 vdepath
[LR
]=strdup(optarg
);
1104 colon
=index(vdepath
[LR
],':');
1107 vdepath
[RL
]=colon
+1;
1109 fprintf(stderr
,"Bad vde_plugs specification.\n");
1114 sscanf(optarg
,"%o",&mgmtmode
);
1120 pidfile
=strdup(optarg
);
1131 * monodir: 0 input LR, 1 mgmtctl, >1 mgmt open conn (mgmtindex==ndirs==1)
1132 * bidir on streams: 0 input LR, 1 input RL, 2 mgmtctl, >2 mgmt open conn (mgmtindex==ndirs==2)
1133 * vdeplug xx:xx : 0 input LR, 1 input RL, 2&3 ctlfd, 4 mgmtctl, > 4 mgmt open conn (mgmtindex>ndirs==2) 5 is console
1134 * vdeplug xx:xx : 0 input LR, 1 input RL, 2&3 ctlfd, 4 console (if not -M)
1135 * vdeplug -:xx : 0 input LR(stdin), 1 input RL, 2 ctlfd, 3 mgmtctl, > 3 mgmt open conn (mgmtindex>ndirs==2)
1136 * vdeplug xx:- : 0 input LR, 1 input RL(stdin), 2 ctlfd, 3 mgmtctl, > 3 mgmt open conn (mgmtindex>ndirs==2)
1139 ndirs
=check_open_fifos_n_plugs(pfd
,outfd
,vdepath
,vdeplug
);
1146 pfd
[npfd
].fd
=vde_ctlfd(vdeplug
[LR
]);
1147 pfd
[npfd
].events
=POLLIN
| POLLHUP
;
1151 pfd
[npfd
].fd
=vde_ctlfd(vdeplug
[RL
]);
1152 pfd
[npfd
].events
=POLLIN
| POLLHUP
;
1157 int mgmtfd
=openmgmt(mgmt
);
1159 pfd
[mgmtindex
].fd
=mgmtfd
;
1160 pfd
[mgmtindex
].events
=POLLIN
| POLLHUP
;
1165 openlog(progname
, LOG_PID
, 0);
1167 } else if (vdeplug
[LR
] && vdeplug
[RL
]) { // console mode
1169 pfd
[npfd
].fd
=STDIN_FILENO
;
1170 pfd
[npfd
].events
=POLLIN
| POLLHUP
;
1174 /* saves current path in pidfile_path, because otherwise with daemonize() we
1176 if(getcwd(pidfile_path
, PATH_MAX
-1) == NULL
) {
1177 printlog(LOG_ERR
, "getcwd: %s", strerror(errno
));
1180 strcat(pidfile_path
, "/");
1181 if (daemonize
&& daemon(0, 0)) {
1182 printlog(LOG_ERR
,"daemon: %s",strerror(errno
));
1186 /* once here, we're sure we're the true process which will continue as a
1187 * server: save PID file if needed */
1188 if(pidfile
) save_pidfile();
1191 printlog(LOG_INFO
,"%s: bidirectional vdeplug filter L=%s R=%s starting...",progname
,
1192 (*vdepath
[LR
])?vdepath
[LR
]:"DEFAULT_SWITCH",
1193 (*vdepath
[RL
])?vdepath
[RL
]:"DEFAULT_SWITCH");
1195 printlog(LOG_INFO
,"%s: bidirectional filter starting...",progname
);
1197 printlog(LOG_INFO
,"%s: monodirectional filter starting...",progname
);
1202 pfd
[0].events
|= POLLIN
;
1203 if (speed
[LR
] > 0) {
1206 gettimeofday(&tv
,NULL
);
1207 if (timercmp(&tv
, &nextspeed
[LR
], <)) {
1208 timersub(&nextspeed
[LR
],&tv
,&tv
);
1209 speeddelay
=tv
.tv_sec
*1000 + tv
.tv_usec
/1000;
1210 if (speeddelay
> 0) {
1211 pfd
[0].events
&= ~POLLIN
;
1212 if (speeddelay
< delay
|| delay
< 0) delay
=speeddelay
;
1217 pfd
[1].events
|= POLLIN
;
1218 if (speed
[RL
] > 0) {
1221 if (timercmp(&tv
, &nextspeed
[RL
], <)) {
1222 gettimeofday(&tv
,NULL
);
1223 timersub(&nextspeed
[RL
],&tv
,&tv
);
1224 speeddelay
=tv
.tv_sec
*1000 + tv
.tv_usec
/1000;
1225 if (speeddelay
> 0) {
1226 pfd
[1].events
&= ~POLLIN
;
1227 if (speeddelay
< delay
|| delay
< 0) delay
=speeddelay
;
1232 n
=poll(pfd
,npfd
,delay
);
1233 if (pfd
[0].revents
& POLLHUP
|| (ndirs
>1 && pfd
[1].revents
& POLLHUP
))
1235 if (pfd
[0].revents
& POLLIN
) {
1238 if (ndirs
>1 && pfd
[1].revents
& POLLIN
) {
1241 if (n
>0) { // if there are already events to handle (performance: packet switching first)
1242 int mgmtfdstart
=consoleindex
;
1243 if (mgmtindex
>= 0) mgmtfdstart
=mgmtindex
+1;
1244 if (mgmtfdstart
>= 0 && npfd
> mgmtfdstart
) {
1246 for (i
=mgmtfdstart
;i
<npfd
;i
++) {
1247 if (pfd
[i
].revents
& POLLIN
&& mgmtcommand(pfd
[i
].fd
) < 0)
1248 pfd
[i
].revents
|= POLLHUP
;
1249 if (pfd
[i
].revents
) n
--;
1251 for (i
=mgmtfdstart
;i
<npfd
;i
++) {
1252 if (pfd
[i
].revents
& POLLHUP
)
1253 npfd
=delmgmtconn(i
,pfd
,npfd
);
1256 if (mgmtindex
>= 0) {
1257 if (pfd
[mgmtindex
].revents
!= 0) {
1258 npfd
=newmgmtconn(pfd
[mgmtindex
].fd
,pfd
,npfd
);
1262 /* if (n>0) // if there are already pending events, it means that a ctlfd has hunged up