Gaussian distribution for range values, Gilbert bursty model for packet loss.
[vde.git] / vde-2 / src / wirefilter.c
blob9fe7c7df6c7bce5b3fb335c1f4f6fde0e32126a0
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).
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <getopt.h>
17 #include <errno.h>
18 #include <libgen.h>
19 #include <syslog.h>
20 #include <fcntl.h>
21 #include <time.h>
22 #include <signal.h>
23 #include <math.h>
24 #include <stdarg.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
31 #include <config.h>
32 #include <vde.h>
33 #include <vdecommon.h>
34 #include <libvdeplug.h>
36 #if defined(VDE_DARWIN) || defined(VDE_FREEBSD)
37 # include <limits.h>
38 # if defined HAVE_SYSLIMITS_H
39 # include <syslimits.h>
40 # elif defined HAVE_SYS_SYSLIMITS_H
41 # include <sys/syslimits.h>
42 # else
43 # error "No syslimits.h found"
44 # endif
45 #endif
47 #define NPIPES 2
48 #define MAXCONN 3
49 static int alternate_stdin;
50 static int alternate_stdout;
51 #define NPFD NPIPES+NPIPES+MAXCONN+1
52 struct pollfd pfd[NPFD];
53 int outfd[NPIPES];
54 char *progname;
55 char *mgmt;
56 int mgmtmode=0700;
57 #define LR 0
58 #define RL 1
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 */
74 #define OK_BURST 0
75 #define FAULTY_BURST 1
76 char loss_status[2];
77 struct timeval nextband[2];
78 struct timeval nextspeed[2];
79 int nofifo;
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];
87 static int logok=0;
89 #define BUFSIZE 2048
90 #define MAXCMD 128
91 #define MGMTMODEARG 129
92 #define DAEMONIZEARG 130
93 #define PIDFILEARG 131
94 #define KILO (1<<10)
95 #define MEGA (1<<20)
96 #define GIGA (1<<30)
98 static void initrand()
100 struct timeval v;
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)
109 if (range == 0)
110 return 0;
111 switch (algorithm) {
112 case ALGO_UNIFORM:
113 return range*((drand48()*2.0)-1.0);
114 case ALGO_GAUSS_NORMAL:
116 double x,y,r2;
117 do {
118 x = (2*drand48())-1;
119 y = (2*drand48())-1;
120 r2=x*x+y*y;
121 } while (r2 >= 1.0);
122 return range * SIGMA * x * sqrt ( (-2 * log(r2)) /r2);
124 default:
125 return 0.0;
129 void printlog(int priority, const char *format, ...)
131 va_list arg;
133 va_start (arg, format);
135 if (logok)
136 vsyslog(priority,format,arg);
137 else {
138 fprintf(stderr,"%s: ",progname);
139 vfprintf(stderr,format,arg);
140 fprintf(stderr,"\n");
142 va_end (arg);
145 static void readdualvalue(char *s,double *val,double *valplus,char *algorithm)
147 double v=0.0;
148 double vplus=0.0;
149 int n;
150 int mult;
151 char algo=ALGO_UNIFORM;
152 n=strlen(s)-1;
153 while ((s[n] == ' ' || s[n] == '\n' || s[n] == '\t') && n>0)
155 s[n]=0;
156 n--;
158 switch (s[n]) {
159 case 'u':
160 case 'U':
161 algo=ALGO_UNIFORM;
162 n--;
163 break;
164 case 'n':
165 case 'N':
166 algo=ALGO_GAUSS_NORMAL;
167 n--;
168 break;
170 switch (s[n]) {
171 case 'k':
172 case 'K':
173 mult=KILO;
174 break;
175 case 'm':
176 case 'M':
177 mult=MEGA;
178 break;
179 case 'g':
180 case 'G':
181 mult=GIGA;
182 break;
183 default:
184 mult=1;
185 break;
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) {
192 val[LR]=v*mult;
193 valplus[LR]=vplus*mult;
194 algorithm[LR]=algo;
195 } else if ((n=sscanf(s,"RL%lf+%lf",&v,&vplus)) > 0) {
196 val[RL]=v*mult;
197 valplus[RL]=vplus*mult;
198 algorithm[RL]=algo;
202 struct packpq {
203 unsigned long long when;
204 int dir;
205 unsigned char *buf;
206 int size;
209 struct packpq **pqh;
210 struct packpq sentinel={0,0,NULL,0};
211 int npq,maxpq;
212 unsigned long long maxwhen;
214 #define PQCHUNK 100
216 static int nextms()
218 if (npq>0) {
219 long long deltat;
220 struct timeval v;
221 gettimeofday(&v,NULL);
222 deltat=pqh[1]->when-(v.tv_sec*1000000+v.tv_usec);
223 return (deltat>0)?(int)(deltat/1000):0;
225 return -1;
228 static inline int outpacket(int dir,const unsigned char *buf,int size)
230 if (vdeplug[1-dir])
231 return vde_send(vdeplug[1-dir],buf+2,size-2,0);
232 else
233 return write(outfd[dir],buf,size);
236 int writepacket(int dir,const unsigned char *buf,int size)
238 /* NOISE */
239 if (noise[dir]+noiseplus[dir] > 0) {
240 double noiseval=noise[dir];
241 int nobit=0;
242 if (noiseplus[dir]) noiseval+=offset_distr(noisealg[dir],noiseplus[dir]);
243 while ((drand48()*8*MEGA) < (size-2)*8*noiseval)
244 nobit++;
245 if (nobit>0) {
246 unsigned char noisedpacket[BUFSIZE];
247 memcpy(noisedpacket,buf,size);
248 while(nobit>0) {
249 int flippedbit=(drand48()*size*8);
250 noisedpacket[(flippedbit >> 3) + 2] ^= 1<<(flippedbit & 0x7);
251 nobit--;
253 return outpacket(dir,noisedpacket,size);
254 } else
255 return outpacket(dir,buf,size);
256 } else
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()
266 struct timeval v;
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--];
271 int k=1;
272 delay_bufsize[pqh[1]->dir] -= pqh[1]->size;
273 writepacket(pqh[1]->dir,pqh[1]->buf,pqh[1]->size);
274 free(pqh[1]->buf);
275 free(pqh[1]);
276 while (k<= npq>>1)
278 int j= k<<1;
279 if (j<npq && pqh[j]->when > pqh[j+1]->when) j++;
280 if (old->when <= pqh[j]->when) {
281 break;
282 } else {
283 pqh[k]=pqh[j];k=j;
286 pqh[k]=old;
290 static void packet_enqueue(int dir,const unsigned char *buf,int size,int delms)
292 struct timeval v;
294 /* CAPACITY */
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)
301 return;
303 /* */
305 struct packpq *new=malloc(sizeof(struct packpq));
306 if (new==NULL) {
307 printlog(LOG_WARNING,"%s: malloc elem %s",progname,strerror(errno));
308 exit (1);
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;
314 new->dir=dir;
315 new->buf=malloc(size);
316 if (new->buf==NULL) {
317 printlog(LOG_WARNING,"%s: malloc elem buf %s",progname,strerror(errno));
318 exit (1);
320 memcpy(new->buf,buf,size);
321 new->size=size;
322 delay_bufsize[dir]+=size;
323 if (pqh==NULL) {
324 pqh=malloc(PQCHUNK*sizeof(struct packpq *));
325 if (pqh==NULL) {
326 printlog(LOG_WARNING,"%s: malloc %s",progname,strerror(errno));
327 exit (1);
329 pqh[0]=&sentinel; maxpq=PQCHUNK;
331 if (npq >= maxpq) {
332 pqh=realloc(pqh,(maxpq=maxpq+PQCHUNK) * sizeof(struct packpq *));
333 if (pqh==NULL) {
334 printlog(LOG_WARNING,"%s: malloc %s",progname,strerror(errno));
335 exit (1);
338 {int k=++npq;
339 while (new->when < pqh[k>>1]->when) {
340 pqh[k]=pqh[k>>1];
341 k >>= 1;
343 pqh[k]=new;
347 void handle_packet(int dir,const unsigned char *buf,int size)
349 /* MTU */
350 /* if the packet is incosistent with the MTU of the line just drop it */
351 if (mtu[dir] > 0 && size > mtu[dir])
352 return;
354 /* LOSS */
355 /* Total packet loss */
356 if (loss[dir]-lossplus[dir] >= 100.0)
357 return;
358 /* probabilistic loss */
359 if (lostburst[dir]+ lostburstplus[dir] > 0) {
360 /* Gilbert model */
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]) {
366 case OK_BURST:
367 if (drand48() < alpha) loss_status[dir]=FAULTY_BURST;
368 break;
369 case FAULTY_BURST:
370 if (drand48() < beta) loss_status[dir]=OK_BURST;
371 break;
373 if (loss_status[dir] != OK_BURST)
374 return;
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)
379 return;
382 /* DUP */
383 /* times is the number of dup packets */
384 int times=1;
385 if (ddup[dir]+ddupplus[dir] > 0) {
386 double dupval=(ddup[dir]+offset_distr(ddupalg[dir],ddupplus[dir]))/100;
387 while (drand48() < dupval)
388 times++;
390 while (times>0) {
391 int banddelay=0;
393 /* SPEED */
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;
401 if (speedval>0) {
402 unsigned int commtime=((unsigned)size)*1000000/((unsigned int)speedval);
403 struct timeval tv;
404 gettimeofday(&tv,NULL);
405 banddelay=commtime/1000;
406 if (timercmp(&tv,&nextspeed[dir], > ))
407 nextspeed[dir]=tv;
408 nextspeed[dir].tv_usec += commtime;
409 nextspeed[dir].tv_sec += nextspeed[dir].tv_usec / 1000000;
410 nextspeed[dir].tv_usec %= 1000000;
414 /* BANDWIDTH */
415 /* band, when band overflows, delay just the delivery */
416 if (band[dir]+bandplus[dir] > 0) {
417 double bandval=band[dir];
418 if (bandplus[dir]) {
419 bandval+=offset_distr(bandalg[dir],bandplus[dir]);
420 if (bandval<=0) return;
422 if (bandval >0) {
423 unsigned int commtime=((unsigned)size)*1000000/((unsigned int)bandval);
424 struct timeval tv;
425 gettimeofday(&tv,NULL);
426 if (timercmp(&tv,&nextband[dir], > )) {
427 nextband[dir]=tv;
428 banddelay=commtime/1000;
429 } else {
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;
436 } else
437 banddelay=-1;
440 /* DELAY */
441 /* line delay */
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;
446 if (delval > 0) {
447 packet_enqueue(dir,buf,size,(int) delval);
448 } else
449 writepacket(dir,buf,size);
450 } else
451 writepacket(dir,buf,size);
453 times--;
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);
466 if (size==0) return;
467 if (rnx[dir]>0) {
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;
472 fragp[dir]+=amount;
473 buf+=amount;
474 size-=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);
478 rnx[dir]=0;
481 while (size > 0) {
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);
484 if (rnx[dir]>1521) {
485 printlog(LOG_WARNING,"%s: Packet length error size %d rnx %d",progname,size,rnx[dir]);
486 rnx[dir]=0;
487 return;
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;
494 fragp[dir]+=size;
495 size=0;
496 } else {
497 handle_packet(dir,buf,rnx[dir]+2);
498 buf+=rnx[dir]+2;
499 size-=rnx[dir]+2;
500 rnx[dir]=0;
505 static void packet_in(int dir)
507 unsigned char buf[BUFSIZE];
508 int n;
509 if(vdeplug[dir]) {
510 n=vde_recv(vdeplug[dir],buf+2,BUFSIZE-2,0);
511 buf[0]=n>>8;
512 buf[1]=n&0xFF;
513 handle_packet(dir,buf,n+2);
514 } else {
515 n=read(pfd[dir].fd,buf,BUFSIZE);
516 if (n == 0)
517 exit (0);
518 splitpacket(buf,n,dir);
522 static int check_open_fifos_n_plugs(struct pollfd *pfd,int *outfd,char *vdepath[],VDECONN *vdeplug[])
524 int ndirs=0;
525 struct stat stfd[NPIPES];
526 char *env_in;
527 char *env_out;
528 env_in=getenv("ALTERNATE_STDIN");
529 env_out=getenv("ALTERNATE_STDOUT");
530 if (env_in != NULL)
531 alternate_stdin=atoi(env_in);
532 if (env_out != NULL)
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));
538 return -1;
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));
546 return -1;
548 pfd[1].fd=vde_datafd(vdeplug[RL]);
549 pfd[1].events=POLLIN | POLLHUP;
551 ndirs=2;
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));
556 return -1;
558 if (fstat(STDOUT_FILENO,&stfd[STDOUT_FILENO]) < 0) {
559 fprintf(stderr,"%s: Error on stdout: %s\n",progname,strerror(errno));
560 return -1;
562 if (!S_ISFIFO(stfd[STDIN_FILENO].st_mode)) {
563 fprintf(stderr,"%s: Error on stdin: %s\n",progname,"it is not a pipe");
564 return -1;
566 if (!S_ISFIFO(stfd[STDOUT_FILENO].st_mode)) {
567 fprintf(stderr,"%s: Error on stdin: %s\n",progname,"it is not a pipe");
568 return -1;
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) {
579 ndirs=1;
580 pfd[0].fd=STDIN_FILENO;
581 pfd[0].events=POLLIN | POLLHUP;
582 outfd[0]=STDOUT_FILENO;
583 } else {
584 if (fstat(outfd[1],&stfd[1]) < 0) {
585 fprintf(stderr,"%s: Error on secondary out: %s\n",progname,strerror(errno));
586 return -1;
588 if (!S_ISFIFO(stfd[0].st_mode)) {
589 fprintf(stderr,"%s: Error on secondary in: %s\n",progname,"it is not a pipe");
590 return -1;
592 if (!S_ISFIFO(stfd[1].st_mode)) {
593 fprintf(stderr,"%s: Error on secondary out: %s\n",progname,"it is not a pipe");
594 return -1;
596 ndirs=2;
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;
605 return ndirs;
608 static void save_pidfile()
610 if(pidfile[0] != '/')
611 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
612 else
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);
618 FILE *f;
620 if(fd == -1) {
621 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
622 exit(1);
625 if((f = fdopen(fd, "w")) == NULL) {
626 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
627 exit(1);
630 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
631 printlog(LOG_ERR, "Error in writing pidfile");
632 exit(1);
635 fclose(f);
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));
643 if (vdeplug[LR])
644 vde_close(vdeplug[LR]);
645 if (vdeplug[RL])
646 vde_close(vdeplug[RL]);
647 if (mgmt)
648 unlink(mgmt);
651 static void sig_handler(int sig)
653 /*fprintf(stderr,"Caught signal %d, cleaning up and exiting", sig);*/
654 cleanup();
655 signal(sig, SIG_DFL);
656 kill(getpid(), sig);
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 },
674 #ifdef VDE_LINUX
675 { SIGPOLL, "SIGPOLL", 1 },
676 #ifdef SIGSTKFLT
677 { SIGSTKFLT, "SIGSTKFLT", 1 },
678 #endif
679 { SIGIO, "SIGIO", 1 },
680 { SIGPWR, "SIGPWR", 1 },
681 #ifdef SIGUNUSED
682 { SIGUNUSED, "SIGUNUSED", 1 },
683 #endif
684 #endif
685 #ifdef VDE_DARWIN
686 { SIGXCPU, "SIGXCPU", 1 },
687 { SIGXFSZ, "SIGXFSZ", 1 },
688 #endif
689 { 0, NULL, 0 }
692 int i;
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,
697 strerror(errno));
700 static int openmgmt(char *mgmt)
702 int mgmtconnfd;
703 struct sockaddr_un sun;
704 int one = 1;
706 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
707 fprintf(stderr,"%s: mgmt socket: %s",progname,strerror(errno));
708 exit(1);
710 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
711 sizeof(one)) < 0){
712 fprintf(stderr,"%s: mgmt setsockopt: %s",progname,strerror(errno));
713 exit(1);
715 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
716 fprintf(stderr,"%s: Setting O_NONBLOCK on mgmt fd: %s",progname,strerror(errno));
717 exit(1);
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));
723 exit(1);
725 chmod(sun.sun_path,mgmtmode);
726 if(listen(mgmtconnfd, 15) < 0){
727 fprintf(stderr,"%s: mgmt listen: %s",progname,strerror(errno));
728 exit(1);
730 return mgmtconnfd;
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)
737 int new;
738 unsigned int len;
739 char buf[MAXCMD];
740 struct sockaddr addr;
741 new = accept(fd, &addr, &len);
742 if(new < 0){
743 printlog(LOG_WARNING,"%s: mgmt accept %s",progname,strerror(errno));
744 return nfds;
746 if (nfds < NPFD) {
747 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
748 write(new,buf,strlen(buf));
749 write(new,prompt,strlen(prompt));
750 pfd[nfds].fd=new;
751 pfd[nfds].events=POLLIN | POLLHUP;
752 return ++nfds;
753 } else {
754 printlog(LOG_WARNING,"%s: too many mgmt connections",progname);
755 close (new);
756 return nfds;
760 static void printoutc(int fd, const char *format, ...)
762 va_list arg;
763 char outbuf[MAXCMD+1];
765 va_start (arg, format);
766 vsnprintf(outbuf,MAXCMD,format,arg);
767 strcat(outbuf,"\n");
768 write(fd,outbuf,strlen(outbuf));
771 static int setdelay(int fd,char *s)
773 readdualvalue(s,delay,delayplus,delayalg);
774 return 0;
777 static int setloss(int fd,char *s)
779 readdualvalue(s,loss,lossplus,lossalg);
780 return 0;
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;
790 return 0;
793 static int setddup(int fd,char *s)
795 readdualvalue(s,ddup,ddupplus,ddupalg);
796 return 0;
799 static int setband(int fd,char *s)
801 readdualvalue(s,band,bandplus,bandalg);
802 return 0;
805 static int setnoise(int fd,char *s)
807 readdualvalue(s,noise,noiseplus,noisealg);
808 return 0;
811 static int setmtu(int fd,char *s)
813 readdualvalue(s,mtu,mtuplus,mtualg);
814 return 0;
817 static int setspeed(int fd,char *s)
819 readdualvalue(s,speed,speedplus,speedalg);
820 return 0;
823 static int setcapacity(int fd,char *s)
825 readdualvalue(s,capacity,capacityplus,capacityalg);
826 return 0;
829 static int setfifo(int fd,char *s)
831 int n=atoi(s);
832 if (n==0)
833 nofifo=1;
834 else
835 nofifo=0;
836 return 0;
839 static int logout(int fd,char *s)
841 return -1;
844 static int doshutdown(int fd,char *s)
846 exit(0);
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");
866 return 0;
869 #define CHARALGO(X) (charalgo[(int)(X)])
870 static int showinfo(int fd,char *s)
872 printoutc(fd, "WireFilter: %sdirectional",(ndirs==2)?"bi":"mono");
873 if (ndirs==2) {
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]);
900 } else {
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);
914 return 0;
917 #define WITHFD 0x80
918 static struct comlist {
919 char *tag;
920 int (*fun)(int fd,char *arg);
921 unsigned char type;
922 } commandlist [] = {
923 {"help", help, WITHFD},
924 {"showinfo",showinfo, WITHFD},
925 {"delay",setdelay, 0},
926 {"loss",setloss, 0},
927 {"lostburst",setlostburst, 0},
928 {"dup",setddup, 0},
929 {"bandwidth",setband, 0},
930 {"band",setband, 0},
931 {"speed",setspeed, 0},
932 {"capacity",setcapacity, 0},
933 {"noise",setnoise, 0},
934 {"mtu",setmtu, 0},
935 {"fifo",setfifo, 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)
944 int rv=ENOSYS;
945 int i;
946 while (*inbuf == ' ' || *inbuf == '\t' || *inbuf == '\n') inbuf++;
947 if (*inbuf != '\0' && *inbuf != '#') {
948 for (i=0; i<NCL
949 && strncmp(commandlist[i].tag,inbuf,strlen(commandlist[i].tag))!=0;
950 i++)
952 if (i<NCL)
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)
960 printoutc(fd,".");
962 printoutc(fd,"1%03d %s",rv,strerror(rv));
963 return rv;
965 return rv;
968 static int mgmtcommand(int fd)
970 char buf[MAXCMD+1];
971 int n,rv;
972 int outfd=fd;
973 n = read(fd, buf, MAXCMD);
974 if (n<0) {
975 printlog(LOG_WARNING,"%s: read from mgmt %s",progname,strerror(errno));
976 return 0;
978 else if (n==0)
979 return -1;
980 else {
981 if (fd==STDIN_FILENO)
982 outfd=STDOUT_FILENO;
983 buf[n]=0;
984 rv=handle_cmd(outfd,buf);
985 if (rv>=0)
986 write(outfd,prompt,strlen(prompt));
987 return rv;
991 static int delmgmtconn(int i,struct pollfd *pfd,int nfds)
993 if (i<nfds) {
994 close(pfd[i].fd);
995 if (pfd[i].fd == 0) /* close stdin implies exit */
996 exit(0);
997 memmove(pfd+i,pfd+i+1,sizeof (struct pollfd) * (nfds-i-1));
998 nfds--;
1000 return nfds;
1003 void usage(void)
1005 fprintf(stderr,"Usage: %s OPTIONS\n"
1006 "\t--help|-h\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"
1016 "\t--nofifo|-N\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"
1020 "\t--daemon\n"
1021 "\t--pidfile pidfile\n"
1022 ,progname);
1023 exit (1);
1026 int main(int argc,char *argv[])
1028 int n;
1029 int npfd;
1030 int option_index;
1031 int mgmtindex=-1;
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'},
1038 {"dup",1 , 0, 'D'},
1039 {"band",1 , 0, 'b'},
1040 {"speed",1 , 0, 's'},
1041 {"capacity",1 , 0, 'c'},
1042 {"noise",1 , 0, 'n'},
1043 {"mtu",1 , 0, 'm'},
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]);
1053 setsighandlers();
1054 atexit(cleanup);
1057 while(1) {
1058 int c;
1059 c = GETOPT_LONG (argc, argv, "hnl:d:M:D:m:b:s:c:v:L:",
1060 long_options, &option_index);
1061 if (c<0)
1062 break;
1063 switch (c) {
1064 case 'h':
1065 usage();
1066 break;
1067 case 'd':
1068 readdualvalue(optarg,delay,delayplus,delayalg);
1069 break;
1070 case 'l':
1071 readdualvalue(optarg,loss,lossplus,lossalg);
1072 break;
1073 case 'L':
1074 readdualvalue(optarg,lostburst,lostburstplus,lostburstalg);
1075 break;
1076 case 'D':
1077 readdualvalue(optarg,ddup,ddupplus,ddupalg);
1078 break;
1079 case 'b':
1080 readdualvalue(optarg,band,bandplus,bandalg);
1081 break;
1082 case 'm':
1083 readdualvalue(optarg,mtu,mtuplus,mtualg);
1084 break;
1085 case 'n':
1086 readdualvalue(optarg,noise,noiseplus,noisealg);
1087 break;
1088 case 's':
1089 readdualvalue(optarg,speed,speedplus,speedalg);
1090 break;
1091 case 'c':
1092 readdualvalue(optarg,capacity,capacityplus,capacityalg);
1093 break;
1094 case 'M':
1095 mgmt=strdup(optarg);
1096 break;
1097 case 'N':
1098 nofifo=1;
1099 break;
1100 case 'v':
1102 char *colon;
1103 vdepath[LR]=strdup(optarg);
1104 colon=index(vdepath[LR],':');
1105 if (colon) {
1106 *colon=0;
1107 vdepath[RL]=colon+1;
1108 } else {
1109 fprintf(stderr,"Bad vde_plugs specification.\n");
1110 usage();
1113 case MGMTMODEARG:
1114 sscanf(optarg,"%o",&mgmtmode);
1115 break;
1116 case DAEMONIZEARG:
1117 daemonize=1;
1118 break;
1119 case PIDFILEARG:
1120 pidfile=strdup(optarg);
1121 break;
1122 default:
1123 usage();
1124 break;
1127 if (optind < argc)
1128 usage();
1130 /* pfd structure:
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);
1141 if (ndirs < 0)
1142 usage();
1144 npfd=ndirs;
1145 if (vdeplug[LR]) {
1146 pfd[npfd].fd=vde_ctlfd(vdeplug[LR]);
1147 pfd[npfd].events=POLLIN | POLLHUP;
1148 npfd++;
1150 if (vdeplug[RL]) {
1151 pfd[npfd].fd=vde_ctlfd(vdeplug[RL]);
1152 pfd[npfd].events=POLLIN | POLLHUP;
1153 npfd++;
1156 if(mgmt != NULL) {
1157 int mgmtfd=openmgmt(mgmt);
1158 mgmtindex=npfd;
1159 pfd[mgmtindex].fd=mgmtfd;
1160 pfd[mgmtindex].events=POLLIN | POLLHUP;
1161 npfd++;
1164 if (daemonize) {
1165 openlog(progname, LOG_PID, 0);
1166 logok=1;
1167 } else if (vdeplug[LR] && vdeplug[RL]) { // console mode
1168 consoleindex=npfd;
1169 pfd[npfd].fd=STDIN_FILENO;
1170 pfd[npfd].events=POLLIN | POLLHUP;
1171 npfd++;
1174 /* saves current path in pidfile_path, because otherwise with daemonize() we
1175 * forget it */
1176 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
1177 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
1178 exit(1);
1180 strcat(pidfile_path, "/");
1181 if (daemonize && daemon(0, 0)) {
1182 printlog(LOG_ERR,"daemon: %s",strerror(errno));
1183 exit(1);
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();
1190 if (vdepath[LR])
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");
1194 else if (ndirs==2)
1195 printlog(LOG_INFO,"%s: bidirectional filter starting...",progname);
1196 else
1197 printlog(LOG_INFO,"%s: monodirectional filter starting...",progname);
1199 initrand();
1200 while(1) {
1201 int delay=nextms();
1202 pfd[0].events |= POLLIN;
1203 if (speed[LR] > 0) {
1204 struct timeval tv;
1205 int speeddelay;
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;
1216 if (ndirs > 1) {
1217 pfd[1].events |= POLLIN;
1218 if (speed[RL] > 0) {
1219 struct timeval tv;
1220 int speeddelay;
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))
1234 exit(0);
1235 if (pfd[0].revents & POLLIN) {
1236 packet_in(LR); n--;
1238 if (ndirs>1 && pfd[1].revents & POLLIN) {
1239 packet_in(RL); n--;
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) {
1245 register int i;
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);
1259 n--;
1262 /* if (n>0) // if there are already pending events, it means that a ctlfd has hunged up
1263 exit(0);*/
1265 packet_dequeue();