new wirefilter -v support
[vde.git] / vde-2 / wirefilter / wirefilter.c
blobcc113260c37f79cebea229ae638206da71de5376
1 /* WIREFILTER (C) 2005 Renzo Davoli
2 * Licensed under the GPLv2
3 * Modified by Ludovico Gardenghi 2005
5 * This filter can be used for testing network protcols.
6 * It is possible to loose, delay or reorder packets.
7 * Options can be set on command line or interactively with a remote interface
8 * on a unix socket (see unixterm).
9 */
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <getopt.h>
16 #include <errno.h>
17 #include <libgen.h>
18 #include <syslog.h>
19 #include <fcntl.h>
20 #include <time.h>
21 #include <signal.h>
22 #include <stdarg.h>
23 #include <sys/time.h>
24 #include <sys/poll.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <config.h>
30 #include <libvdeplug/libvdeplug.h>
32 #include <vde.h>
34 #define NPIPES 2
35 #define MAXCONN 3
36 static int alternate_stdin;
37 static int alternate_stdout;
38 #define NPFD NPIPES+NPIPES+MAXCONN+1
39 struct pollfd pfd[NPFD];
40 int outfd[NPIPES];
41 char *progname;
42 char *mgmt;
43 int mgmtmode=0700;
44 #define LR 0
45 #define RL 1
46 double loss[2],lossplus[2];
47 double delay[2],delayplus[2];
48 double ddup[2],ddupplus[2];
49 double band[2],bandplus[2];
50 double speed[2],speedplus[2];
51 double capacity[2],capacityplus[2];
52 double noise[2],noiseplus[2];
53 double mtu[2],mtuplus[2];
54 struct timeval nextband[2];
55 struct timeval nextspeed[2];
56 int nofifo;
57 int ndirs; //1 mono directional, 2 bi directional filter (always 2 with -v)
58 int delay_bufsize[2]; //total size of delayed packets
59 char *vdepath[2]; //path of the directly connected switched (via vde_plug)
60 VDECONN *vdeplug[2]; //vde_plug connections (if NULL stdin/stdout)
61 int daemonize; // daemon mode
62 static char *pidfile = NULL;
63 static char pidfile_path[PATH_MAX];
64 static int logok=0;
66 #define BUFSIZE 2048
67 #define MAXCMD 128
68 #define MGMTMODEARG 129
69 #define DAEMONIZEARG 130
70 #define PIDFILEARG 131
71 #define KILO (1<<10)
72 #define MEGA (1<<20)
73 #define GIGA (1<<30)
75 void printlog(int priority, const char *format, ...)
77 va_list arg;
79 va_start (arg, format);
81 if (logok)
82 vsyslog(priority,format,arg);
83 else {
84 fprintf(stderr,"%s: ",progname);
85 vfprintf(stderr,format,arg);
86 fprintf(stderr,"\n");
88 va_end (arg);
91 static void readdualvalue(char *s,double *val,double *valplus)
93 double v=0.0;
94 double vplus=0.0;
95 int n;
96 int mult;
97 n=strlen(s)-1;
98 while ((s[n] == ' ' || s[n] == '\n' || s[n] == '\t') && n>0)
100 s[n]=0;
101 n--;
103 switch (s[n]) {
104 case 'k':
105 case 'K':
106 mult=KILO;
107 break;
108 case 'm':
109 case 'M':
110 mult=MEGA;
111 break;
112 case 'g':
113 case 'G':
114 mult=GIGA;
115 break;
116 default:
117 mult=1;
118 break;
120 if ((n=sscanf(s,"%lf+%lf",&v,&vplus)) > 0) {
121 val[LR]=val[RL]=v*mult;
122 valplus[LR]=valplus[RL]=vplus*mult;
123 } else if ((n=sscanf(s,"LR%lf+%lf",&v,&vplus)) > 0) {
124 val[LR]=v*mult;
125 valplus[LR]=vplus*mult;
126 } else if ((n=sscanf(s,"RL%lf+%lf",&v,&vplus)) > 0) {
127 val[RL]=v*mult;
128 valplus[RL]=vplus*mult;
132 struct packpq {
133 unsigned long long when;
134 int dir;
135 unsigned char *buf;
136 int size;
139 struct packpq **pqh;
140 struct packpq sentinel={0,0,NULL,0};
141 int npq,maxpq;
142 unsigned long long maxwhen;
144 #define PQCHUNK 100
146 static int nextms()
148 if (npq>0) {
149 long long deltat;
150 struct timeval v;
151 gettimeofday(&v,NULL);
152 deltat=pqh[1]->when-(v.tv_sec*1000000+v.tv_usec);
153 return (deltat>0)?(int)(deltat/1000):0;
155 return -1;
158 static inline int outpacket(int dir,const unsigned char *buf,int size)
160 if (vdeplug[1-dir])
161 vde_send(vdeplug[1-dir],buf+2,size-2,0);
162 else
163 write(outfd[dir],buf,size);
166 int writepacket(int dir,const unsigned char *buf,int size)
168 /* NOISE */
169 if (noise[dir]+noiseplus[dir] > 0) {
170 double noiseval=noise[dir];
171 int nobit=0;
172 if (noiseplus) noiseval+=((drand48()*2.0)-1.0)*noiseplus[dir];
173 while ((drand48()*8*MEGA) < (size-2)*8*noiseval)
174 nobit++;
175 if (nobit>0) {
176 unsigned char noisedpacket[BUFSIZE];
177 memcpy(noisedpacket,buf,size);
178 while(nobit>0) {
179 int flippedbit=(drand48()*size*8);
180 noisedpacket[(flippedbit >> 3) + 2] ^= 1<<(flippedbit & 0x7);
181 nobit--;
183 return outpacket(dir,noisedpacket,size);
184 } else
185 return outpacket(dir,buf,size);
186 } else
187 return outpacket(dir,buf,size);
190 /* packet queues are priority queues implemented on a heap.
191 * enqueue time = dequeue time = O(log n) max&mean
194 static void packet_dequeue()
196 struct timeval v;
197 gettimeofday(&v,NULL);
198 unsigned long long now=v.tv_sec*1000000+v.tv_usec;
199 while (npq>0 && pqh[1]->when <= now) {
200 struct packpq *old=pqh[npq--];
201 int k=1;
202 delay_bufsize[pqh[1]->dir] -= pqh[1]->size;
203 writepacket(pqh[1]->dir,pqh[1]->buf,pqh[1]->size);
204 free(pqh[1]->buf);
205 free(pqh[1]);
206 while (k<= npq>>1)
208 int j= k<<1;
209 if (j<npq && pqh[j]->when > pqh[j+1]->when) j++;
210 if (old->when <= pqh[j]->when) {
211 break;
212 } else {
213 pqh[k]=pqh[j];k=j;
216 pqh[k]=old;
220 static void packet_enqueue(int dir,const unsigned char *buf,int size,int delms)
222 struct timeval v;
224 /* CAPACITY */
225 if (capacity[dir]+capacityplus[dir] > 0) {
226 double capval=capacity[dir];
227 if (capacityplus[dir])
228 capval+=((drand48()*2.0)-1.0)*capacityplus[dir];
229 if ((delay_bufsize[dir]+size) > capval)
230 return;
232 /* */
234 struct packpq *new=malloc(sizeof(struct packpq));
235 if (new==NULL) {
236 printlog(LOG_WARNING,"%s: malloc elem %s",progname,strerror(errno));
237 exit (1);
239 gettimeofday(&v,NULL);
240 new->when=v.tv_sec * 1000000 + v.tv_usec + delms * 1000;
241 if (new->when > maxwhen) maxwhen=new->when;
242 if (!nofifo && new->when < maxwhen) new->when=maxwhen;
243 new->dir=dir;
244 new->buf=malloc(size);
245 if (new->buf==NULL) {
246 printlog(LOG_WARNING,"%s: malloc elem buf %s",progname,strerror(errno));
247 exit (1);
249 memcpy(new->buf,buf,size);
250 new->size=size;
251 delay_bufsize[dir]+=size;
252 if (pqh==NULL) {
253 pqh=malloc(PQCHUNK*sizeof(struct packpq *));
254 if (pqh==NULL) {
255 printlog(LOG_WARNING,"%s: malloc %s",progname,strerror(errno));
256 exit (1);
258 pqh[0]=&sentinel; maxpq=PQCHUNK;
260 if (npq >= maxpq) {
261 pqh=realloc(pqh,(maxpq=maxpq+PQCHUNK) * sizeof(struct packpq *));
262 if (pqh==NULL) {
263 printlog(LOG_WARNING,"%s: malloc %s",progname,strerror(errno));
264 exit (1);
267 {int k=++npq;
268 while (new->when < pqh[k>>1]->when) {
269 pqh[k]=pqh[k>>1];
270 k >>= 1;
272 pqh[k]=new;
276 void handle_packet(int dir,const unsigned char *buf,int size)
278 /* MTU */
279 if (mtu[dir] > 0 && size > mtu[dir])
280 return;
282 /* LOSS */
283 if (loss[dir]-lossplus[dir] >= 100.0)
284 return;
285 if (loss[dir]+lossplus[dir] > 0) {
286 double losval=(loss[dir]+((drand48()*2.0)-1.0)*lossplus[dir])/100;
287 if (drand48() < losval)
288 return;
291 /* DUP */
292 int times=1;
293 if (ddup[dir]+ddupplus[dir] > 0) {
294 double dupval=(ddup[dir]+((drand48()*2.0)-1.0)*ddupplus[dir])/100;
295 while (drand48() < dupval)
296 times++;
298 while (times>0) {
299 int banddelay=0;
301 /* SPEED */
302 if (speed[dir]+speedplus[dir] > 0) {
303 double speedval=speed[dir];
304 if (speedplus[dir]) {
305 speedval+=((drand48()*2.0)-1.0)*speedplus[dir];
306 if (speedval<=0) return;
308 if (speed>0) {
309 unsigned int commtime=((unsigned)size)*1000000/((unsigned int)speedval);
310 struct timeval tv;
311 gettimeofday(&tv,NULL);
312 banddelay=commtime/1000;
313 if (timercmp(&tv,&nextspeed[dir], > ))
314 nextspeed[dir]=tv;
315 nextspeed[dir].tv_usec += commtime;
316 nextspeed[dir].tv_sec += nextspeed[dir].tv_usec / 1000000;
317 nextspeed[dir].tv_usec %= 1000000;
321 /* BANDWIDTH */
322 if (band[dir]+bandplus[dir] > 0) {
323 double bandval=band[dir];
324 if (bandplus[dir]) {
325 bandval+=((drand48()*2.0)-1.0)*bandplus[dir];
326 if (bandval<=0) return;
328 if (band>0) {
329 unsigned int commtime=((unsigned)size)*1000000/((unsigned int)bandval);
330 struct timeval tv;
331 gettimeofday(&tv,NULL);
332 if (timercmp(&tv,&nextband[dir], > )) {
333 nextband[dir]=tv;
334 banddelay=commtime/1000;
335 } else {
336 timersub(&nextband[dir],&tv,&tv);
337 banddelay=tv.tv_sec*1000 + (tv.tv_usec + commtime)/1000;
339 nextband[dir].tv_usec += commtime;
340 nextband[dir].tv_sec += nextband[dir].tv_usec / 1000000;
341 nextband[dir].tv_usec %= 1000000;
342 } else
343 banddelay=-1;
346 /* DELAY */
347 if (banddelay >= 0) {
348 if (banddelay > 0 || delay[dir]+delayplus[dir] > 0) {
349 double delval=(delay[dir]+((drand48()*2.0)-1.0)*delayplus[dir]);
350 delval=(delval >= 0)?delval+banddelay:banddelay;
351 if (delval > 0) {
352 packet_enqueue(dir,buf,size,(int) delval);
353 } else
354 writepacket(dir,buf,size);
355 } else
356 writepacket(dir,buf,size);
358 times--;
362 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
364 static void splitpacket(const unsigned char *buf,int size,int dir)
366 static unsigned char fragment[BUFSIZE][2];
367 static unsigned char *fragp[2];
368 static unsigned int rnx[2],remaining[2];
370 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",progname,rnx[dir],remaining[dir],size);
371 if (size==0) return;
372 if (rnx[dir]>0) {
373 register int amount=MIN(remaining[dir],size);
374 //fprintf(stderr,"%s: fragment amount %d\n",progname,amount);
375 memcpy(fragp[dir],buf,amount);
376 remaining[dir]-=amount;
377 fragp[dir]+=amount;
378 buf+=amount;
379 size-=amount;
380 if (remaining[dir]==0) {
381 //fprintf(stderr,"%s: delivered defrag %d\n",progname,rnx[dir]);
382 handle_packet(dir,fragment[dir],rnx[dir]+2);
383 rnx[dir]=0;
386 while (size > 0) {
387 rnx[dir]=(buf[0]<<8)+buf[1];
388 //fprintf(stderr,"%s: packet %d size %d %x %x dir %d\n",progname,rnx[dir],size-2,buf[0],buf[1],dir);
389 if (rnx[dir]>1521) {
390 printlog(LOG_WARNING,"%s: Packet length error size %d rnx %d",progname,size,rnx[dir]);
391 rnx[dir]=0;
392 return;
394 if (rnx[dir]+2 > size) {
395 //fprintf(stderr,"%s: begin defrag %d\n",progname,rnx[dir]);
396 fragp[dir]=fragment[dir];
397 memcpy(fragp[dir],buf,size);
398 remaining[dir]=rnx[dir]+2-size;
399 fragp[dir]+=size;
400 size=0;
401 } else {
402 handle_packet(dir,buf,rnx[dir]+2);
403 buf+=rnx[dir]+2;
404 size-=rnx[dir]+2;
405 rnx[dir]=0;
410 static void packet_in(int dir)
412 unsigned char buf[BUFSIZE];
413 int n;
414 if(vdeplug[dir]) {
415 n=vde_recv(vdeplug[dir],buf+2,BUFSIZE-2,0);
416 buf[0]=n>>8;
417 buf[1]=n&0xFF;
418 handle_packet(dir,buf,n+2);
419 } else {
420 n=read(pfd[dir].fd,buf,BUFSIZE);
421 if (n == 0)
422 exit (0);
423 splitpacket(buf,n,dir);
427 static void initrand()
429 struct timeval v;
430 gettimeofday(&v,NULL);
431 srand48(v.tv_sec ^ v.tv_usec ^ getpid());
434 static int check_open_fifos_n_plugs(struct pollfd *pfd,int *outfd,char *vdepath[],VDECONN *vdeplug[])
436 int ndirs=0;
437 struct stat stfd[NPIPES];
438 char *env_in;
439 char *env_out;
440 env_in=getenv("ALTERNATE_STDIN");
441 env_out=getenv("ALTERNATE_STDOUT");
442 if (env_in != NULL)
443 alternate_stdin=atoi(env_in);
444 if (env_out != NULL)
445 alternate_stdout=atoi(env_out);
446 if (vdepath[0]) { // -v selected
447 if (strcmp(vdepath[0],"-") != 0) {
448 if((vdeplug[LR]=vde_open(vdepath[0],"vde_crosscable",NULL))==NULL){
449 fprintf(stderr,"vdeplug %s: %s\n",vdepath[0],strerror(errno));
450 return -1;
452 pfd[0].fd=vde_datafd(vdeplug[LR]);
453 pfd[0].events=POLLIN | POLLHUP;
455 if (strcmp(vdepath[1],"-") != 0) {
456 if((vdeplug[RL]=vde_open(vdepath[1],"vde_crosscable",NULL))==NULL){
457 fprintf(stderr,"vdeplug %s: %s\n",vdepath[1],strerror(errno));
458 return -1;
460 pfd[1].fd=vde_datafd(vdeplug[RL]);
461 pfd[1].events=POLLIN | POLLHUP;
463 ndirs=2;
465 if (vdeplug[LR] == NULL || vdeplug[RL] == NULL) {
466 if (fstat(STDIN_FILENO,&stfd[STDIN_FILENO]) < 0) {
467 fprintf(stderr,"%s: Error on stdin: %s\n",progname,strerror(errno));
468 return -1;
470 if (fstat(STDOUT_FILENO,&stfd[STDOUT_FILENO]) < 0) {
471 fprintf(stderr,"%s: Error on stdout: %s\n",progname,strerror(errno));
472 return -1;
474 if (!S_ISFIFO(stfd[STDIN_FILENO].st_mode)) {
475 fprintf(stderr,"%s: Error on stdin: %s\n",progname,"it is not a pipe");
476 return -1;
478 if (!S_ISFIFO(stfd[STDOUT_FILENO].st_mode)) {
479 fprintf(stderr,"%s: Error on stdin: %s\n",progname,"it is not a pipe");
480 return -1;
482 if (vdeplug[RL] != NULL) { /* -v -:xxx */
483 pfd[0].fd=STDIN_FILENO;
484 pfd[0].events=POLLIN | POLLHUP;
485 outfd[1]=STDOUT_FILENO;
486 } else if (vdeplug[LR] != NULL) { /* -v xxx:- */
487 pfd[1].fd=STDIN_FILENO;
488 pfd[1].events=POLLIN | POLLHUP;
489 outfd[0]=STDOUT_FILENO;
490 } else if (env_in == NULL || fstat(alternate_stdin,&stfd[0]) < 0) {
491 ndirs=1;
492 pfd[0].fd=STDIN_FILENO;
493 pfd[0].events=POLLIN | POLLHUP;
494 outfd[0]=STDOUT_FILENO;
495 } else {
496 if (fstat(outfd[1],&stfd[1]) < 0) {
497 fprintf(stderr,"%s: Error on secondary out: %s\n",progname,strerror(errno));
498 return -1;
500 if (!S_ISFIFO(stfd[0].st_mode)) {
501 fprintf(stderr,"%s: Error on secondary in: %s\n",progname,"it is not a pipe");
502 return -1;
504 if (!S_ISFIFO(stfd[1].st_mode)) {
505 fprintf(stderr,"%s: Error on secondary out: %s\n",progname,"it is not a pipe");
506 return -1;
508 ndirs=2;
509 pfd[LR].fd=STDIN_FILENO;
510 pfd[LR].events=POLLIN | POLLHUP;
511 outfd[LR]=alternate_stdout;
512 pfd[RL].fd=alternate_stdin;
513 pfd[RL].events=POLLIN | POLLHUP;
514 outfd[RL]=STDOUT_FILENO;
517 return ndirs;
520 static void save_pidfile()
522 if(pidfile[0] != '/')
523 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
524 else
525 strcpy(pidfile_path, pidfile);
527 int fd = open(pidfile_path,
528 O_WRONLY | O_CREAT | O_EXCL,
529 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
530 FILE *f;
532 if(fd == -1) {
533 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
534 exit(1);
537 if((f = fdopen(fd, "w")) == NULL) {
538 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
539 exit(1);
542 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
543 printlog(LOG_ERR, "Error in writing pidfile");
544 exit(1);
547 fclose(f);
550 static void cleanup(void)
552 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
553 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
555 if (vdeplug[LR])
556 vde_close(vdeplug[LR]);
557 if (vdeplug[RL])
558 vde_close(vdeplug[RL]);
559 if (mgmt)
560 unlink(mgmt);
563 static void sig_handler(int sig)
565 /*fprintf(stderr,"Caught signal %d, cleaning up and exiting", sig);*/
566 cleanup();
567 signal(sig, SIG_DFL);
568 kill(getpid(), sig);
571 static void setsighandlers()
573 /* setting signal handlers.
574 * * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
575 * * * ignores all the others signals which could cause termination. */
576 struct { int sig; const char *name; int ignore; } signals[] = {
577 { SIGHUP, "SIGHUP", 0 },
578 { SIGINT, "SIGINT", 0 },
579 { SIGPIPE, "SIGPIPE", 1 },
580 { SIGALRM, "SIGALRM", 1 },
581 { SIGTERM, "SIGTERM", 0 },
582 { SIGUSR1, "SIGUSR1", 1 },
583 { SIGUSR2, "SIGUSR2", 1 },
584 { SIGPROF, "SIGPROF", 1 },
585 { SIGVTALRM, "SIGVTALRM", 1 },
586 #ifdef VDE_LINUX
587 { SIGPOLL, "SIGPOLL", 1 },
588 #ifdef SIGSTKFLT
589 { SIGSTKFLT, "SIGSTKFLT", 1 },
590 #endif
591 { SIGIO, "SIGIO", 1 },
592 { SIGPWR, "SIGPWR", 1 },
593 #ifdef SIGUNUSED
594 { SIGUNUSED, "SIGUNUSED", 1 },
595 #endif
596 #endif
597 #ifdef VDE_DARWIN
598 { SIGXCPU, "SIGXCPU", 1 },
599 { SIGXFSZ, "SIGXFSZ", 1 },
600 #endif
601 { 0, NULL, 0 }
604 int i;
605 for(i = 0; signals[i].sig != 0; i++)
606 if(signal(signals[i].sig,
607 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
608 fprintf(stderr,"%s: Setting handler for %s: %s", progname, signals[i].name,
609 strerror(errno));
612 static int openmgmt(char *mgmt)
614 int mgmtconnfd;
615 struct sockaddr_un sun;
616 int one = 1;
618 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
619 fprintf(stderr,"%s: mgmt socket: %s",progname,strerror(errno));
620 exit(1);
622 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
623 sizeof(one)) < 0){
624 fprintf(stderr,"%s: mgmt setsockopt: %s",progname,strerror(errno));
625 exit(1);
627 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
628 fprintf(stderr,"%s: Setting O_NONBLOCK on mgmt fd: %s",progname,strerror(errno));
629 exit(1);
631 sun.sun_family = PF_UNIX;
632 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s",mgmt);
633 if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
634 fprintf(stderr,"%s: mgmt bind %s",progname,strerror(errno));
635 exit(1);
637 chmod(sun.sun_path,mgmtmode);
638 if(listen(mgmtconnfd, 15) < 0){
639 fprintf(stderr,"%s: mgmt listen: %s",progname,strerror(errno));
640 exit(1);
642 return mgmtconnfd;
645 static char header[]="\nVDE wirefilter V.%s\n(C) R.Davoli 2005,2006 - GPLv2\n";
646 static char prompt[]="\nVDEwf:";
647 static int newmgmtconn(int fd,struct pollfd *pfd,int nfds)
649 int new;
650 unsigned int len;
651 char buf[MAXCMD];
652 struct sockaddr addr;
653 new = accept(fd, &addr, &len);
654 if(new < 0){
655 printlog(LOG_WARNING,"%s: mgmt accept %s",progname,strerror(errno));
656 return nfds;
658 if (nfds < NPFD) {
659 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
660 write(new,buf,strlen(buf));
661 write(new,prompt,strlen(prompt));
662 pfd[nfds].fd=new;
663 pfd[nfds].events=POLLIN | POLLHUP;
664 return ++nfds;
665 } else {
666 printlog(LOG_WARNING,"%s: too many mgmt connections",progname);
667 close (new);
668 return nfds;
672 static void printoutc(int fd, const char *format, ...)
674 va_list arg;
675 char outbuf[MAXCMD+1];
677 va_start (arg, format);
678 vsnprintf(outbuf,MAXCMD,format,arg);
679 strcat(outbuf,"\n");
680 write(fd,outbuf,strlen(outbuf));
683 static int setdelay(int fd,char *s)
685 readdualvalue(s,delay,delayplus);
686 return 0;
689 static int setloss(int fd,char *s)
691 readdualvalue(s,loss,lossplus);
692 return 0;
695 static int setddup(int fd,char *s)
697 readdualvalue(s,ddup,ddupplus);
698 return 0;
701 static int setband(int fd,char *s)
703 readdualvalue(s,band,bandplus);
704 return 0;
707 static int setnoise(int fd,char *s)
709 readdualvalue(s,noise,noiseplus);
710 return 0;
713 static int setmtu(int fd,char *s)
715 readdualvalue(s,mtu,mtuplus);
716 return 0;
719 static int setspeed(int fd,char *s)
721 readdualvalue(s,speed,speedplus);
722 return 0;
725 static int setcapacity(int fd,char *s)
727 readdualvalue(s,capacity,capacityplus);
728 return 0;
731 static int setfifo(int fd,char *s)
733 int n=atoi(s);
734 if (n==0)
735 nofifo=1;
736 else
737 nofifo=0;
738 return 0;
741 static int logout(int fd,char *s)
743 return -1;
746 static int doshutdown(int fd,char *s)
748 exit(0);
752 static int help(int fd,char *s)
754 printoutc(fd, "help: print a summary of mgmt commands");
755 printoutc(fd, "showinfo: show status and parameter values");
756 printoutc(fd, "loss: set loss percentage");
757 printoutc(fd, "delay: set delay ms");
758 printoutc(fd, "dup: set dup packet percentage");
759 printoutc(fd, "bandwidth: set channel bandwidth bytes/sec");
760 printoutc(fd, "speed: set interface speed bytes/sec");
761 printoutc(fd, "noise: set noise factor bits/Mbyte");
762 printoutc(fd, "mtu: set channel MTU (bytes)");
763 printoutc(fd, "capacity: set channel capacity (bytes)");
764 printoutc(fd, "fifo: set channel fifoness");
765 printoutc(fd, "shutdown: shut the channel down");
766 printoutc(fd, "logout: log out from this mgmt session");
767 return 0;
770 static int showinfo(int fd,char *s)
772 printoutc(fd, "WireFilter: %sdirectional",(ndirs==2)?"bi":"mono");
773 if (ndirs==2) {
774 printoutc(fd, "Loss L->R %g+%g R->L %g+%g",loss[LR],lossplus[LR],loss[RL],lossplus[RL]);
775 printoutc(fd, "Delay L->R %g+%g R->L %g+%g",delay[LR],delayplus[LR],delay[RL],delayplus[RL]);
776 printoutc(fd, "Dup L->R %g+%g R->L %g+%g",ddup[LR],ddupplus[LR],ddup[RL],ddupplus[RL]);
777 printoutc(fd, "Bandw L->R %g+%g R->L %g+%g",band[LR],bandplus[LR],band[RL],bandplus[RL]);
778 printoutc(fd, "Speed L->R %g+%g R->L %g+%g",speed[LR],speedplus[LR],speed[RL],speedplus[RL]);
779 printoutc(fd, "Noise L->R %g+%g R->L %g+%g",noise[LR],noiseplus[LR],noise[RL],noiseplus[RL]);
780 printoutc(fd, "MTU L->R %g R->L %g ",mtu[LR],mtu[RL]);
781 printoutc(fd, "Cap. L->R %g+%g R->L %g+%g",capacity[LR],capacityplus[LR],capacity[RL],capacityplus[RL]);
782 printoutc(fd, "Current Delay Queue size: L->R %d R->L %d ",delay_bufsize[LR],delay_bufsize[RL]);
783 } else {
784 printoutc(fd, "Loss %g+%g",loss[0],lossplus[0]);
785 printoutc(fd, "Delay %g+%g",delay[0],delayplus[0]);
786 printoutc(fd, "Dup %g+%g",ddup[0],ddupplus[0]);
787 printoutc(fd, "Bandw %g+%g",band[0],bandplus[0]);
788 printoutc(fd, "Speed %g+%g",speed[0],speedplus[0]);
789 printoutc(fd, "Noise %g+%g",noise[0],noiseplus[0]);
790 printoutc(fd, "MTU %g",mtu[0]);
791 printoutc(fd, "Cap. %g+%g",capacity[0],capacityplus[0]);
792 printoutc(fd, "Current Delay Queue size: %d",delay_bufsize[0]);
794 printoutc(fd,"Fifoness %s",(nofifo == 0)?"TRUE":"FALSE");
795 printoutc(fd,"Waiting packets in delay queues %d",npq);
796 return 0;
799 static struct comlist {
800 char *tag;
801 int (*fun)(int fd,char *arg);
802 } commandlist [] = {
803 {"help", help},
804 {"showinfo",showinfo},
805 {"delay",setdelay},
806 {"loss",setloss},
807 {"dup",setddup},
808 {"bandwidth",setband},
809 {"band",setband},
810 {"speed",setspeed},
811 {"capacity",setcapacity},
812 {"noise",setnoise},
813 {"mtu",setmtu},
814 {"fifo",setfifo},
815 {"logout",logout},
816 {"shutdown",doshutdown}
819 #define NCL sizeof(commandlist)/sizeof(struct comlist)
821 static int handle_cmd(int fd,char *inbuf)
823 int rv=ENOSYS;
824 int i;
825 while (*inbuf == ' ' || *inbuf == '\t' || *inbuf == '\n') inbuf++;
826 if (*inbuf != '\0' && *inbuf != '#') {
827 for (i=0; i<NCL
828 && strncmp(commandlist[i].tag,inbuf,strlen(commandlist[i].tag))!=0;
829 i++)
831 if (i<NCL)
833 inbuf += strlen(commandlist[i].tag);
834 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
835 rv=commandlist[i].fun(fd,inbuf);
837 printoutc(fd,"1%03d %s",rv,strerror(rv));
838 return rv;
840 return rv;
843 static int mgmtcommand(int fd)
845 char buf[MAXCMD+1];
846 int n,rv;
847 int outfd=fd;
848 n = read(fd, buf, MAXCMD);
849 if (n<0) {
850 printlog(LOG_WARNING,"%s: read from mgmt %s",progname,strerror(errno));
851 return 0;
853 else if (n==0)
854 return -1;
855 else {
856 if (fd==STDIN_FILENO)
857 outfd=STDOUT_FILENO;
858 buf[n]=0;
859 rv=handle_cmd(outfd,buf);
860 if (rv>=0)
861 write(outfd,prompt,strlen(prompt));
862 return rv;
866 static int delmgmtconn(int i,struct pollfd *pfd,int nfds)
868 if (i<nfds) {
869 close(pfd[i].fd);
870 if (pfd[i].fd == 0) /* close stdin implies exit */
871 exit(0);
872 memmove(pfd+i,pfd+i+1,sizeof (struct pollfd) * (nfds-i-1));
873 nfds--;
875 return nfds;
878 void usage(void)
880 fprintf(stderr,"Usage: %s OPTIONS\n"
881 "\t--help|-h\n"
882 "\t--loss|-l loss_percentage\n"
883 "\t--delay|-d delay_ms\n"
884 "\t--dup|-D dup_percentage\n"
885 "\t--band|-b bandwidth(bytes/s)\n"
886 "\t--speed|-s interface_speed(bytes/s)\n"
887 "\t--capacity|-c delay_channel_capacity\n"
888 "\t--noise|-n noise_bits/megabye\n"
889 "\t--mtu|-m mtu_size\n"
890 "\t--nofifo|-N\n"
891 "\t--mgmt|-M management_socket\n"
892 "\t--mgmtmode management_permission(octal)\n"
893 "\t--vde-plug plug1:plug2 | -v plug1:plug2\n"
894 "\t--daemon\n"
895 "\t--pidfile pidfile\n"
896 ,progname);
897 exit (1);
900 int main(int argc,char *argv[])
902 int n;
903 int npfd;
904 int option_index;
905 int mgmtindex=-1;
906 int consoleindex=-1;
907 static struct option long_options[] = {
908 {"help",0 , 0, 'h'},
909 {"loss", 1, 0, 'l'},
910 {"delay",1 , 0, 'd'},
911 {"dup",1 , 0, 'D'},
912 {"band",1 , 0, 'b'},
913 {"speed",1 , 0, 's'},
914 {"capacity",1 , 0, 'c'},
915 {"noise",1 , 0, 'n'},
916 {"mtu",1 , 0, 'm'},
917 {"nofifo",0 , 0, 'N'},
918 {"mgmt", 1, 0, 'M'},
919 {"mgmtmode", 1, 0, MGMTMODEARG},
920 {"vde-plug",1,0,'v'},
921 {"daemon",0 , 0, DAEMONIZEARG},
922 {"pidfile", 1, 0, PIDFILEARG}
924 progname=basename(argv[0]);
926 setsighandlers();
927 atexit(cleanup);
930 while(1) {
931 int c;
932 c = GETOPT_LONG (argc, argv, "hnl:d:M:D:m:b:s:c:v:",
933 long_options, &option_index);
934 if (c<0)
935 break;
936 switch (c) {
937 case 'h':
938 usage();
939 break;
940 case 'd':
941 readdualvalue(optarg,delay,delayplus);
942 break;
943 case 'l':
944 readdualvalue(optarg,loss,lossplus);
945 break;
946 case 'D':
947 readdualvalue(optarg,ddup,ddupplus);
948 break;
949 case 'b':
950 readdualvalue(optarg,band,bandplus);
951 break;
952 case 'm':
953 readdualvalue(optarg,mtu,mtuplus);
954 break;
955 case 'n':
956 readdualvalue(optarg,noise,noiseplus);
957 break;
958 case 's':
959 readdualvalue(optarg,speed,speedplus);
960 break;
961 case 'c':
962 readdualvalue(optarg,capacity,capacityplus);
963 break;
964 case 'M':
965 mgmt=strdup(optarg);
966 break;
967 case 'N':
968 nofifo=1;
969 break;
970 case 'v':
972 char *colon;
973 vdepath[LR]=strdup(optarg);
974 colon=index(vdepath[LR],':');
975 if (colon) {
976 *colon=0;
977 vdepath[RL]=colon+1;
978 } else {
979 fprintf(stderr,"Bad vde_plugs specification.\n");
980 usage();
983 case MGMTMODEARG:
984 sscanf(optarg,"%o",&mgmtmode);
985 break;
986 case DAEMONIZEARG:
987 daemonize=1;
988 break;
989 case PIDFILEARG:
990 pidfile=strdup(optarg);
991 break;
992 default:
993 usage();
994 break;
997 if (optind < argc)
998 usage();
1000 /* pfd structure:
1001 * monodir: 0 input LR, 1 mgmtctl, >1 mgmt open conn (mgmtindex==ndirs==1)
1002 * bidir on streams: 0 input LR, 1 input RL, 2 mgmtctl, >2 mgmt open conn (mgmtindex==ndirs==2)
1003 * vdeplug xx:xx : 0 input LR, 1 input RL, 2&3 ctlfd, 4 mgmtctl, > 4 mgmt open conn (mgmtindex>ndirs==2) 5 is console
1004 * vdeplug xx:xx : 0 input LR, 1 input RL, 2&3 ctlfd, 4 console (if not -M)
1005 * vdeplug -:xx : 0 input LR(stdin), 1 input RL, 2 ctlfd, 3 mgmtctl, > 3 mgmt open conn (mgmtindex>ndirs==2)
1006 * vdeplug xx:- : 0 input LR, 1 input RL(stdin), 2 ctlfd, 3 mgmtctl, > 3 mgmt open conn (mgmtindex>ndirs==2)
1009 ndirs=check_open_fifos_n_plugs(pfd,outfd,vdepath,vdeplug);
1011 if (ndirs < 0)
1012 usage();
1014 npfd=ndirs;
1015 if (vdeplug[LR]) {
1016 pfd[npfd].fd=vde_ctlfd(vdeplug[LR]);
1017 pfd[npfd].events=POLLIN | POLLHUP;
1018 npfd++;
1020 if (vdeplug[RL]) {
1021 pfd[npfd].fd=vde_ctlfd(vdeplug[RL]);
1022 pfd[npfd].events=POLLIN | POLLHUP;
1023 npfd++;
1026 if(mgmt != NULL) {
1027 int mgmtfd=openmgmt(mgmt);
1028 mgmtindex=npfd;
1029 pfd[mgmtindex].fd=mgmtfd;
1030 pfd[mgmtindex].events=POLLIN | POLLHUP;
1031 npfd++;
1034 if (daemonize) {
1035 openlog(progname, LOG_PID, 0);
1036 logok=1;
1037 } else if (vdeplug[LR] && vdeplug[RL]) { // console mode
1038 consoleindex=npfd;
1039 pfd[npfd].fd=STDIN_FILENO;
1040 pfd[npfd].events=POLLIN | POLLHUP;
1041 npfd++;
1044 /* saves current path in pidfile_path, because otherwise with daemonize() we
1045 * forget it */
1046 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
1047 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
1048 exit(1);
1050 strcat(pidfile_path, "/");
1051 if (daemonize && daemon(0, 0)) {
1052 printlog(LOG_ERR,"daemon: %s",strerror(errno));
1053 exit(1);
1056 /* once here, we're sure we're the true process which will continue as a
1057 * server: save PID file if needed */
1058 if(pidfile) save_pidfile();
1060 if (vdepath[LR])
1061 printlog(LOG_INFO,"%s: bidirectional vdeplug filter L=%s R=%s starting...",progname,
1062 (*vdepath[LR])?vdepath[LR]:"DEFAULT_SWITCH",
1063 (*vdepath[RL])?vdepath[RL]:"DEFAULT_SWITCH");
1064 else if (ndirs==2)
1065 printlog(LOG_INFO,"%s: bidirectional filter starting...",progname);
1066 else
1067 printlog(LOG_INFO,"%s: monodirectional filter starting...",progname);
1069 initrand();
1070 while(1) {
1071 int delay=nextms();
1072 pfd[0].events |= POLLIN;
1073 if (speed[LR] > 0) {
1074 struct timeval tv;
1075 int speeddelay;
1076 gettimeofday(&tv,NULL);
1077 if (timercmp(&tv, &nextspeed[LR], <)) {
1078 timersub(&nextspeed[LR],&tv,&tv);
1079 speeddelay=tv.tv_sec*1000 + tv.tv_usec/1000;
1080 if (speeddelay > 0) {
1081 pfd[0].events &= ~POLLIN;
1082 if (speeddelay < delay || delay < 0) delay=speeddelay;
1086 if (ndirs > 1) {
1087 pfd[1].events |= POLLIN;
1088 if (speed[RL] > 0) {
1089 struct timeval tv;
1090 int speeddelay;
1091 if (timercmp(&tv, &nextspeed[RL], <)) {
1092 gettimeofday(&tv,NULL);
1093 timersub(&nextspeed[RL],&tv,&tv);
1094 speeddelay=tv.tv_sec*1000 + tv.tv_usec/1000;
1095 if (speeddelay > 0) {
1096 pfd[1].events &= ~POLLIN;
1097 if (speeddelay < delay || delay < 0) delay=speeddelay;
1102 n=poll(pfd,npfd,delay);
1103 if (pfd[0].revents & POLLHUP || (ndirs>1 && pfd[1].revents & POLLHUP))
1104 exit(0);
1105 if (pfd[0].revents & POLLIN) {
1106 packet_in(LR); n--;
1108 if (ndirs>1 && pfd[1].revents & POLLIN) {
1109 packet_in(RL); n--;
1111 if (n>0) { // if there are already events to handle (performance: packet switching first)
1112 int mgmtfdstart=consoleindex;
1113 if (mgmtindex >= 0) {
1114 if (pfd[mgmtindex].revents != 0) {
1115 npfd=newmgmtconn(pfd[mgmtindex].fd,pfd,npfd);
1116 n--;
1118 mgmtfdstart=mgmtindex+1;
1120 if (mgmtfdstart >= 0 && npfd > mgmtfdstart) {
1121 register int i;
1122 for (i=mgmtfdstart;i<npfd;i++) {
1123 if (pfd[i].revents & POLLHUP ||
1124 (pfd[i].revents & POLLIN && mgmtcommand(pfd[i].fd) < 0))
1125 npfd=delmgmtconn(i,pfd,npfd);
1126 if (pfd[i].revents) n--;
1129 /* if (n>0) // if there are already pending events, it means that a ctlfd has hunged up
1130 exit(0);*/
1132 packet_dequeue();