Bugfix: packetq count could became negative
[vde.git] / vde-2 / wirefilter.c
blob48615415de612a9f1df3ba81e9480fecfaca9e5f
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 <fcntl.h>
19 #include <time.h>
20 #include <signal.h>
21 #include <stdarg.h>
22 #include <sys/time.h>
23 #include <sys/poll.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <config.h>
30 #include <vde.h>
32 #define NPIPES 2
33 #define MAXCONN 3
34 #define STDIN_ALTFILENO 3
35 #define STDOUT_ALTFILENO 4
36 #define NPFD NPIPES+MAXCONN+1
37 struct pollfd pfd[NPFD];
38 int outfd[NPIPES];
39 char *progname;
40 char *mgmt;
41 int mgmtmode=0700;
42 #define LR 0
43 #define RL 1
44 double loss[2],lossplus[2];
45 double delay[2],delayplus[2];
46 double ddup[2],ddupplus[2];
47 double band[2],bandplus[2];
48 double speed[2],speedplus[2];
49 double capacity[2],capacityplus[2];
50 double noise[2],noiseplus[2];
51 double mtu[2],mtuplus[2];
52 struct timeval nextband[2];
53 struct timeval nextspeed[2];
54 int nofifo;
55 int ndirs;
56 int bufsize[2];
58 #define BUFSIZE 2048
59 #define MAXCMD 128
60 #define MGMTMODEARG 129
61 #define KILO (1<<10)
62 #define MEGA (1<<20)
63 #define GIGA (1<<30)
65 static void readdualvalue(char *s,double *val,double *valplus)
67 double v=0.0;
68 double vplus=0.0;
69 int n;
70 int mult;
71 n=strlen(s)-1;
72 while ((s[n] == ' ' || s[n] == '\n' || s[n] == '\t') && n>0)
74 s[n]=0;
75 n--;
77 switch (s[n]) {
78 case 'k':
79 case 'K':
80 mult=KILO;
81 break;
82 case 'm':
83 case 'M':
84 mult=MEGA;
85 break;
86 case 'g':
87 case 'G':
88 mult=GIGA;
89 break;
90 default:
91 mult=1;
92 break;
94 if ((n=sscanf(s,"%lf+%lf",&v,&vplus)) > 0) {
95 val[LR]=val[RL]=v*mult;
96 valplus[LR]=valplus[RL]=vplus*mult;
97 } else if ((n=sscanf(s,"LR%lf+%lf",&v,&vplus)) > 0) {
98 val[LR]=v*mult;
99 valplus[LR]=vplus*mult;
100 } else if ((n=sscanf(s,"RL%lf+%lf",&v,&vplus)) > 0) {
101 val[RL]=v*mult;
102 valplus[RL]=vplus*mult;
106 struct packpq {
107 unsigned long long when;
108 int dir;
109 unsigned char *buf;
110 int size;
113 struct packpq **pqh;
114 struct packpq sentinel={0,0,NULL,0};
115 int npq,maxpq;
116 unsigned long long maxwhen;
118 #define PQCHUNK 100
120 static int nextms()
122 if (npq>0) {
123 long long deltat;
124 struct timeval v;
125 gettimeofday(&v,NULL);
126 deltat=pqh[1]->when-(v.tv_sec*1000000+v.tv_usec);
127 return (deltat>0)?(int)(deltat/1000):0;
129 return -1;
132 int writepacket(int dir,const unsigned char *buf,int size)
134 /* NOISE */
135 if (noise[dir]+noiseplus[dir] > 0) {
136 double noiseval=noise[dir];
137 int nobit=0;
138 if (noiseplus) noiseval+=((drand48()*2.0)-1.0)*noiseplus[dir];
139 while ((drand48()*8*MEGA) < (size-2)*8*noiseval)
140 nobit++;
141 if (nobit>0) {
142 unsigned char noisedpacket[BUFSIZE];
143 memcpy(noisedpacket,buf,size);
144 while(nobit>0) {
145 int flippedbit=(drand48()*size*8);
146 noisedpacket[(flippedbit >> 3) + 2] ^= 1<<(flippedbit & 0x7);
147 nobit--;
149 return write(outfd[dir],noisedpacket,size);
150 } else
151 return write(outfd[dir],buf,size);
152 } else
153 return write(outfd[dir],buf,size);
156 /* packet queues are priority queues implemented on a heap.
157 * enqueue time = dequeue time = O(log n) max&mean
160 static void packet_dequeue()
162 struct timeval v;
163 gettimeofday(&v,NULL);
164 unsigned long long now=v.tv_sec*1000000+v.tv_usec;
165 while (npq>0 && pqh[1]->when <= now) {
166 struct packpq *old=pqh[npq--];
167 int k=1;
168 bufsize[pqh[1]->dir] -= pqh[1]->size;
169 writepacket(pqh[1]->dir,pqh[1]->buf,pqh[1]->size);
170 free(pqh[1]->buf);
171 free(pqh[1]);
172 while (k<= npq>>1)
174 int j= k<<1;
175 if (j<npq && pqh[j]->when > pqh[j+1]->when) j++;
176 if (old->when <= pqh[j]->when) {
177 break;
178 } else {
179 pqh[k]=pqh[j];k=j;
182 pqh[k]=old;
186 static void packet_enqueue(int dir,const unsigned char *buf,int size,int delms)
188 struct timeval v;
190 /* CAPACITY */
191 if (capacity[dir]+capacityplus[dir] > 0) {
192 double capval=capacity[dir];
193 if (capacityplus[dir])
194 capval+=((drand48()*2.0)-1.0)*capacityplus[dir];
195 if ((bufsize[dir]+size) > capval)
196 return;
198 /* */
200 struct packpq *new=malloc(sizeof(struct packpq));
201 if (new==NULL) {
202 fprintf(stderr,"%s: malloc elem %s\n",progname,strerror(errno));
203 exit (1);
205 gettimeofday(&v,NULL);
206 new->when=v.tv_sec * 1000000 + v.tv_usec + delms * 1000;
207 if (new->when > maxwhen) maxwhen=new->when;
208 if (!nofifo && new->when < maxwhen) new->when=maxwhen;
209 new->dir=dir;
210 new->buf=malloc(size);
211 if (new->buf==NULL) {
212 fprintf(stderr,"%s: malloc elem buf %s\n",progname,strerror(errno));
213 exit (1);
215 memcpy(new->buf,buf,size);
216 new->size=size;
217 bufsize[dir]+=size;
218 if (pqh==NULL) {
219 pqh=malloc(PQCHUNK*sizeof(struct packpq *));
220 if (pqh==NULL) {
221 fprintf(stderr,"%s: malloc %s\n",progname,strerror(errno));
222 exit (1);
224 pqh[0]=&sentinel; maxpq=PQCHUNK;
226 if (npq >= maxpq) {
227 pqh=realloc(pqh,(maxpq=maxpq+PQCHUNK) * sizeof(struct packpq *));
228 if (pqh==NULL) {
229 fprintf(stderr,"%s: malloc %s\n",progname,strerror(errno));
230 exit (1);
233 {int k=++npq;
234 while (new->when < pqh[k>>1]->when) {
235 pqh[k]=pqh[k>>1];
236 k >>= 1;
238 pqh[k]=new;
242 void handle_packet(int dir,const unsigned char *buf,int size)
244 /* MTU */
245 if (mtu[dir] > 0 && size > mtu[dir])
246 return;
248 /* LOSS */
249 if (loss[dir]-lossplus[dir] >= 100.0)
250 return;
251 if (loss[dir]+lossplus[dir] > 0) {
252 double losval=(loss[dir]+((drand48()*2.0)-1.0)*lossplus[dir])/100;
253 if (drand48() < losval)
254 return;
257 /* DUP */
258 int times=1;
259 if (ddup[dir]+ddupplus[dir] > 0) {
260 double dupval=(ddup[dir]+((drand48()*2.0)-1.0)*ddupplus[dir])/100;
261 while (drand48() < dupval)
262 times++;
264 while (times>0) {
265 int banddelay=0;
267 /* SPEED */
268 if (speed[dir]+speedplus[dir] > 0) {
269 double speedval=speed[dir];
270 if (speedplus[dir]) {
271 speedval+=((drand48()*2.0)-1.0)*speedplus[dir];
272 if (speedval<=0) return;
274 if (speed>0) {
275 unsigned int commtime=((unsigned)size)*1000000/((unsigned int)speedval);
276 struct timeval tv;
277 gettimeofday(&tv,NULL);
278 banddelay=commtime/1000;
279 if (timercmp(&tv,&nextspeed[dir], > ))
280 nextspeed[dir]=tv;
281 nextspeed[dir].tv_usec += commtime;
282 nextspeed[dir].tv_sec += nextspeed[dir].tv_usec / 1000000;
283 nextspeed[dir].tv_usec %= 1000000;
287 /* BANDWIDTH */
288 if (band[dir]+bandplus[dir] > 0) {
289 double bandval=band[dir];
290 if (bandplus[dir]) {
291 bandval+=((drand48()*2.0)-1.0)*bandplus[dir];
292 if (bandval<=0) return;
294 if (band>0) {
295 unsigned int commtime=((unsigned)size)*1000000/((unsigned int)bandval);
296 struct timeval tv;
297 gettimeofday(&tv,NULL);
298 if (timercmp(&tv,&nextband[dir], > )) {
299 nextband[dir]=tv;
300 banddelay=commtime/1000;
301 } else {
302 timersub(&nextband[dir],&tv,&tv);
303 banddelay=tv.tv_sec*1000 + (tv.tv_usec + commtime)/1000;
305 nextband[dir].tv_usec += commtime;
306 nextband[dir].tv_sec += nextband[dir].tv_usec / 1000000;
307 nextband[dir].tv_usec %= 1000000;
308 } else
309 banddelay=-1;
312 /* DELAY */
313 if (banddelay >= 0) {
314 if (banddelay > 0 || delay[dir]+delayplus[dir] > 0) {
315 double delval=(delay[dir]+((drand48()*2.0)-1.0)*delayplus[dir]);
316 delval=(delval >= 0)?delval+banddelay:banddelay;
317 if (delval > 0) {
318 packet_enqueue(dir,buf,size,(int) delval);
319 } else
320 writepacket(dir,buf,size);
321 } else
322 writepacket(dir,buf,size);
324 times--;
328 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
330 static void splitpacket(const unsigned char *buf,int size,int dir)
332 static unsigned char fragment[BUFSIZE][2];
333 static unsigned char *fragp[2];
334 static unsigned int rnx[2],remaining[2];
336 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",progname,rnx[dir],remaining[dir],size);
337 if (size==0) return;
338 if (rnx[dir]>0) {
339 register int amount=MIN(remaining[dir],size);
340 //fprintf(stderr,"%s: fragment amount %d\n",progname,amount);
341 memcpy(fragp[dir],buf,amount);
342 remaining[dir]-=amount;
343 fragp[dir]+=amount;
344 buf+=amount;
345 size-=amount;
346 if (remaining[dir]==0) {
347 //fprintf(stderr,"%s: delivered defrag %d\n",progname,rnx[dir]);
348 handle_packet(dir,fragment[dir],rnx[dir]+2);
349 rnx[dir]=0;
352 while (size > 0) {
353 rnx[dir]=(buf[0]<<8)+buf[1];
354 //fprintf(stderr,"%s: packet %d size %d %x %x dir %d\n",progname,rnx[dir],size-2,buf[0],buf[1],dir);
355 if (rnx[dir]>1521) {
356 fprintf(stderr,"%s: Packet length error size %d rnx %d\n",progname,size,rnx[dir]);
357 rnx[dir]=0;
358 return;
360 if (rnx[dir]+2 > size) {
361 //fprintf(stderr,"%s: begin defrag %d\n",progname,rnx[dir]);
362 fragp[dir]=fragment[dir];
363 memcpy(fragp[dir],buf,size);
364 remaining[dir]=rnx[dir]+2-size;
365 fragp[dir]+=size;
366 size=0;
367 } else {
368 handle_packet(dir,buf,rnx[dir]+2);
369 buf+=rnx[dir]+2;
370 size-=rnx[dir]+2;
371 rnx[dir]=0;
376 static void packet_in(int dir)
378 unsigned char buf[BUFSIZE];
379 int n;
380 n=read(pfd[dir].fd,buf,BUFSIZE);
381 if (n == 0)
382 exit (0);
383 splitpacket(buf,n,dir);
386 static void initrand()
388 struct timeval v;
389 gettimeofday(&v,NULL);
390 srand48(v.tv_sec ^ v.tv_usec ^ getpid());
393 static int check_open_fifos(struct pollfd *pfd,int *outfd)
395 int ndirs;
396 struct stat stfd[NPIPES];
397 if (fstat(STDIN_FILENO,&stfd[STDIN_FILENO]) < 0) {
398 fprintf(stderr,"%s: Error on stdin: %s\n",progname,strerror(errno));
399 return -1;
401 if (fstat(STDOUT_FILENO,&stfd[STDOUT_FILENO]) < 0) {
402 fprintf(stderr,"%s: Error on stdout: %s\n",progname,strerror(errno));
403 return -1;
405 if (!S_ISFIFO(stfd[STDIN_FILENO].st_mode)) {
406 fprintf(stderr,"%s: Error on stdin: %s\n",progname,"it is not a pipe");
407 return -1;
409 if (!S_ISFIFO(stfd[STDOUT_FILENO].st_mode)) {
410 fprintf(stderr,"%s: Error on stdin: %s\n",progname,"it is not a pipe");
411 return -1;
413 if (fstat(STDIN_ALTFILENO,&stfd[0]) < 0) {
414 ndirs=1;
415 pfd[0].fd=STDIN_FILENO;
416 pfd[0].events=POLLIN | POLLHUP;
417 pfd[0].revents=0;
418 outfd[0]=STDOUT_FILENO;
419 } else {
420 if (fstat(outfd[1],&stfd[1]) < 0) {
421 fprintf(stderr,"%s: Error on secondary out: %s\n",progname,strerror(errno));
422 return -1;
424 if (!S_ISFIFO(stfd[0].st_mode)) {
425 fprintf(stderr,"%s: Error on secondary in: %s\n",progname,"it is not a pipe");
426 return -1;
428 if (!S_ISFIFO(stfd[1].st_mode)) {
429 fprintf(stderr,"%s: Error on secondary out: %s\n",progname,"it is not a pipe");
430 return -1;
432 ndirs=2;
433 pfd[LR].fd=STDIN_FILENO;
434 pfd[LR].events=POLLIN | POLLHUP;
435 pfd[LR].revents=0;
436 outfd[LR]=STDOUT_ALTFILENO;
437 pfd[RL].fd=STDIN_ALTFILENO;
438 pfd[RL].events=POLLIN | POLLHUP;
439 pfd[RL].revents=0;
440 outfd[RL]=STDOUT_FILENO;
442 return ndirs;
445 static void cleanup(void)
447 if (mgmt)
448 unlink(mgmt);
451 static void sig_handler(int sig)
453 /*fprintf(stderr,"Caught signal %d, cleaning up and exiting", sig);*/
454 cleanup();
455 signal(sig, SIG_DFL);
456 kill(getpid(), sig);
459 static void setsighandlers()
461 /* setting signal handlers.
462 * * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
463 * * * ignores all the others signals which could cause termination. */
464 struct { int sig; const char *name; int ignore; } signals[] = {
465 { SIGHUP, "SIGHUP", 0 },
466 { SIGINT, "SIGINT", 0 },
467 { SIGPIPE, "SIGPIPE", 1 },
468 { SIGALRM, "SIGALRM", 1 },
469 { SIGTERM, "SIGTERM", 0 },
470 { SIGUSR1, "SIGUSR1", 1 },
471 { SIGUSR2, "SIGUSR2", 1 },
472 { SIGPROF, "SIGPROF", 1 },
473 { SIGVTALRM, "SIGVTALRM", 1 },
474 #ifdef VDE_LINUX
475 { SIGPOLL, "SIGPOLL", 1 },
476 { SIGSTKFLT, "SIGSTKFLT", 1 },
477 { SIGIO, "SIGIO", 1 },
478 { SIGPWR, "SIGPWR", 1 },
479 { SIGUNUSED, "SIGUNUSED", 1 },
480 #endif
481 #ifdef VDE_DARWIN
482 { SIGXCPU, "SIGXCPU", 1 },
483 { SIGXFSZ, "SIGXFSZ", 1 },
484 #endif
485 { 0, NULL, 0 }
488 int i;
489 for(i = 0; signals[i].sig != 0; i++)
490 if(signal(signals[i].sig,
491 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
492 fprintf(stderr,"%s: Setting handler for %s: %s", progname, signals[i].name,
493 strerror(errno));
496 static int openmgmt(char *mgmt)
498 int mgmtconnfd;
499 struct sockaddr_un sun;
500 int one = 1;
502 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
503 fprintf(stderr,"%s: mgmt socket: %s",progname,strerror(errno));
504 exit(1);
506 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
507 sizeof(one)) < 0){
508 fprintf(stderr,"%s: mgmt setsockopt: %s",progname,strerror(errno));
509 exit(1);
511 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
512 fprintf(stderr,"%s: Setting O_NONBLOCK on mgmt fd: %s",progname,strerror(errno));
513 exit(1);
515 sun.sun_family = PF_UNIX;
516 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s",mgmt);
517 if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
518 fprintf(stderr,"%s: mgmt bind %s",progname,strerror(errno));
519 exit(1);
521 chmod(sun.sun_path,mgmtmode);
522 if(listen(mgmtconnfd, 15) < 0){
523 fprintf(stderr,"%s: mgmt listen: %s",progname,strerror(errno));
524 exit(1);
526 return mgmtconnfd;
529 static char header[]="\nVDE wirefilter V.%s\n(C) R.Davoli 2005 - GPLv2\n";
530 static char prompt[]="\nVDEwf:";
531 static int newmgmtconn(int fd,struct pollfd *pfd,int nfds)
533 int new;
534 unsigned int len;
535 char buf[MAXCMD];
536 struct sockaddr addr;
537 new = accept(fd, &addr, &len);
538 if(new < 0){
539 fprintf(stderr,"%s: mgmt accept %s",progname,strerror(errno));
540 return nfds;
542 if (nfds < NPFD) {
543 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
544 write(new,buf,strlen(buf));
545 write(new,prompt,strlen(prompt));
546 pfd[nfds].fd=new;
547 pfd[nfds].events=POLLIN | POLLHUP;
548 pfd[nfds].revents=0;
549 return ++nfds;
550 } else {
551 fprintf(stderr,"%s: too many mgmt connections",progname);
552 close (new);
553 return nfds;
557 static void printoutc(int fd, const char *format, ...)
559 va_list arg;
560 char outbuf[MAXCMD+1];
562 va_start (arg, format);
563 vsnprintf(outbuf,MAXCMD,format,arg);
564 strcat(outbuf,"\n");
565 write(fd,outbuf,strlen(outbuf));
568 static int setdelay(int fd,char *s)
570 readdualvalue(s,delay,delayplus);
571 return 0;
574 static int setloss(int fd,char *s)
576 readdualvalue(s,loss,lossplus);
577 return 0;
580 static int setddup(int fd,char *s)
582 readdualvalue(s,ddup,ddupplus);
583 return 0;
586 static int setband(int fd,char *s)
588 readdualvalue(s,band,bandplus);
589 return 0;
592 static int setnoise(int fd,char *s)
594 readdualvalue(s,noise,noiseplus);
595 return 0;
598 static int setmtu(int fd,char *s)
600 readdualvalue(s,mtu,mtuplus);
601 return 0;
604 static int setspeed(int fd,char *s)
606 readdualvalue(s,speed,speedplus);
607 return 0;
610 static int setcapacity(int fd,char *s)
612 readdualvalue(s,capacity,capacityplus);
613 return 0;
616 static int setfifo(int fd,char *s)
618 int n=atoi(s);
619 if (n==0)
620 nofifo=1;
621 else
622 nofifo=0;
623 return 0;
626 static int logout(int fd,char *s)
628 return -1;
631 static int doshutdown(int fd,char *s)
633 exit(0);
637 static int help(int fd,char *s)
639 printoutc(fd, "help: print a summary of mgmt commands");
640 printoutc(fd, "showinfo: show status and parameter values");
641 printoutc(fd, "loss: set loss percentage");
642 printoutc(fd, "delay: set delay ms");
643 printoutc(fd, "dup: set dup packet percentage");
644 printoutc(fd, "bandwidth: set channel bandwidth bytes/sec");
645 printoutc(fd, "speed: set interface speed bytes/sec");
646 printoutc(fd, "noise: set noise factor bits/Mbyte");
647 printoutc(fd, "mtu: set channel MTU (bytes)");
648 printoutc(fd, "capacity: set channel capacity (bytes)");
649 printoutc(fd, "fifo: set channel fifoness");
650 printoutc(fd, "shutdown: shut the channel down");
651 printoutc(fd, "logout: log out from this mgmt session");
652 return 0;
655 static int showinfo(int fd,char *s)
657 printoutc(fd, "WireFilter: %sdirectional",(ndirs==2)?"bi":"mono");
658 if (ndirs==2) {
659 printoutc(fd, "Loss L->R %g+%g R->L %g+%g",loss[LR],lossplus[LR],loss[RL],lossplus[RL]);
660 printoutc(fd, "Delay L->R %g+%g R->L %g+%g",delay[LR],delayplus[LR],delay[RL],delayplus[RL]);
661 printoutc(fd, "Dup L->R %g+%g R->L %g+%g",ddup[LR],ddupplus[LR],ddup[RL],ddupplus[RL]);
662 printoutc(fd, "Bandw L->R %g+%g R->L %g+%g",band[LR],bandplus[LR],band[RL],bandplus[RL]);
663 printoutc(fd, "Speed L->R %g+%g R->L %g+%g",speed[LR],speedplus[LR],speed[RL],speedplus[RL]);
664 printoutc(fd, "Noise L->R %g+%g R->L %g+%g",noise[LR],noiseplus[LR],noise[RL],noiseplus[RL]);
665 printoutc(fd, "MTU L->R %g R->L %g ",mtu[LR],mtu[RL]);
666 printoutc(fd, "Cap. L->R %g+%g R->L %g+%g",capacity[LR],capacityplus[LR],capacity[RL],capacityplus[RL]);
667 printoutc(fd, "Current Delay Queue size: L->R %d R->L %d ",bufsize[LR],bufsize[RL]);
668 } else {
669 printoutc(fd, "Loss %g+%g",loss[0],lossplus[0]);
670 printoutc(fd, "Delay %g+%g",delay[0],delayplus[0]);
671 printoutc(fd, "Dup %g+%g",ddup[0],ddupplus[0]);
672 printoutc(fd, "Bandw %g+%g",band[0],bandplus[0]);
673 printoutc(fd, "Speed %g+%g",speed[0],speedplus[0]);
674 printoutc(fd, "Noise %g+%g",noise[0],noiseplus[0]);
675 printoutc(fd, "MTU %g",mtu[0]);
676 printoutc(fd, "Cap. %g+%g",capacity[0],capacityplus[0]);
677 printoutc(fd, "Current Delay Queue size: %d",bufsize[0]);
679 printoutc(fd,"Fifoness %s",(nofifo == 0)?"TRUE":"FALSE");
680 printoutc(fd,"Waiting packets in delay queues %d",npq);
681 return 0;
684 static struct comlist {
685 char *tag;
686 int (*fun)(int fd,char *arg);
687 } commandlist [] = {
688 {"help", help},
689 {"showinfo",showinfo},
690 {"delay",setdelay},
691 {"loss",setloss},
692 {"dup",setddup},
693 {"bandwidth",setband},
694 {"band",setband},
695 {"speed",setspeed},
696 {"capacity",setcapacity},
697 {"noise",setnoise},
698 {"mtu",setmtu},
699 {"fifo",setfifo},
700 {"logout",logout},
701 {"shutdown",doshutdown}
704 #define NCL sizeof(commandlist)/sizeof(struct comlist)
706 static int handle_cmd(int fd,char *inbuf)
708 int rv=ENOSYS;
709 int i;
710 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
711 if (*inbuf != '\0' && *inbuf != '#') {
712 for (i=0; i<NCL
713 && strncmp(commandlist[i].tag,inbuf,strlen(commandlist[i].tag))!=0;
714 i++)
716 if (i<NCL)
718 inbuf += strlen(commandlist[i].tag);
719 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
720 rv=commandlist[i].fun(fd,inbuf);
722 printoutc(fd,"1%03d %s",rv,strerror(rv));
723 return rv;
725 return rv;
728 static int mgmtcommand(int fd)
730 char buf[MAXCMD+1];
731 int n,rv;
732 n = read(fd, buf, MAXCMD);
733 if (n<0) {
734 fprintf(stderr,"%s: read from mgmt %s",progname,strerror(errno));
735 return 0;
737 else if (n==0)
738 return -1;
739 else {
740 buf[n]=0;
741 rv=handle_cmd(fd,buf);
742 if (rv>=0)
743 write(fd,prompt,strlen(prompt));
744 return rv;
748 static int delmgmtconn(int i,struct pollfd *pfd,int nfds)
750 if (i<nfds) {
751 close(pfd[i].fd);
752 memmove(pfd+i,pfd+i+1,sizeof (struct pollfd) * (nfds-i-1));
753 nfds--;
755 return nfds;
758 void usage(void)
760 fprintf(stderr,"Usage: %s OPTIONS\n"
761 "\t--help|-h\n"
762 "\t--loss|-l loss_percentage\n"
763 "\t--delay|-d delay_ms\n"
764 "\t--dup|-D dup_percentage\n"
765 "\t--band|-b bandwidth(bytes/s)\n"
766 "\t--speed|-s interface_speed(bytes/s)\n"
767 "\t--capacity|-c delay_channel_capacity\n"
768 "\t--noise|-n noise_bits/megabye\n"
769 "\t--mtu|-m mtu_size\n"
770 "\t--nofifo|-N\n"
771 "\t--mgmt|-M management_socket\n"
772 "\t--mgmtmode management_permission(octal)\n"
773 ,progname);
774 exit (1);
777 int main(int argc,char *argv[])
779 int n;
780 int npfd;
781 int option_index;
782 int mgmtindex=-1;
783 static struct option long_options[] = {
784 {"help",0 , 0, 'h'},
785 {"loss", 1, 0, 'l'},
786 {"delay",1 , 0, 'd'},
787 {"dup",1 , 0, 'D'},
788 {"band",1 , 0, 'b'},
789 {"speed",1 , 0, 's'},
790 {"capacity",1 , 0, 'c'},
791 {"noise",1 , 0, 'n'},
792 {"mtu",1 , 0, 'm'},
793 {"nofifo",0 , 0, 'N'},
794 {"mgmt", 1, 0, 'M'},
795 {"mgmtmode", 1, 0, MGMTMODEARG}
797 progname=basename(argv[0]);
799 setsighandlers();
800 atexit(cleanup);
802 ndirs=check_open_fifos(pfd,outfd);
803 if (ndirs < 0)
804 usage();
806 while(1) {
807 int c;
808 c = GETOPT_LONG (argc, argv, "hnl:d:M:D:m:b:s:c:",
809 long_options, &option_index);
810 if (c<0)
811 break;
812 switch (c) {
813 case 'h':
814 usage();
815 break;
816 case 'd':
817 readdualvalue(optarg,delay,delayplus);
818 break;
819 case 'l':
820 readdualvalue(optarg,loss,lossplus);
821 break;
822 case 'D':
823 readdualvalue(optarg,ddup,ddupplus);
824 break;
825 case 'b':
826 readdualvalue(optarg,band,bandplus);
827 break;
828 case 'm':
829 readdualvalue(optarg,mtu,mtuplus);
830 break;
831 case 'n':
832 readdualvalue(optarg,noise,noiseplus);
833 break;
834 case 's':
835 readdualvalue(optarg,speed,speedplus);
836 break;
837 case 'c':
838 readdualvalue(optarg,capacity,capacityplus);
839 break;
840 case 'M':
841 mgmt=strdup(optarg);
842 break;
843 case 'N':
844 nofifo=1;
845 break;
846 case MGMTMODEARG:
847 sscanf(optarg,"%o",&mgmtmode);
848 break;
849 default:
850 usage();
851 break;
854 if (optind < argc)
855 usage();
857 if (ndirs==2)
858 fprintf(stderr,"%s: bidirectional filter starting...\n",progname);
859 else
860 fprintf(stderr,"%s: monodirectional filter starting...\n",progname);
862 npfd=ndirs;
864 if(mgmt != NULL) {
865 int mgmtfd=openmgmt(mgmt);
866 mgmtindex=npfd;
867 pfd[mgmtindex].fd=mgmtfd;
868 pfd[mgmtindex].events=POLLIN | POLLHUP;
869 pfd[mgmtindex].revents=0;
870 npfd++;
873 initrand();
874 while(1) {
875 int delay=nextms();
876 pfd[0].events |= POLLIN;
877 if (speed[LR] > 0) {
878 struct timeval tv;
879 int speeddelay;
880 gettimeofday(&tv,NULL);
881 if (timercmp(&tv, &nextspeed[LR], <)) {
882 timersub(&nextspeed[LR],&tv,&tv);
883 speeddelay=tv.tv_sec*1000 + tv.tv_usec/1000;
884 if (speeddelay > 0) {
885 pfd[0].events &= ~POLLIN;
886 if (speeddelay < delay || delay < 0) delay=speeddelay;
890 if (ndirs > 1) {
891 pfd[1].events |= POLLIN;
892 if (speed[RL] > 0) {
893 struct timeval tv;
894 int speeddelay;
895 if (timercmp(&tv, &nextspeed[RL], <)) {
896 gettimeofday(&tv,NULL);
897 timersub(&nextspeed[RL],&tv,&tv);
898 speeddelay=tv.tv_sec*1000 + tv.tv_usec/1000;
899 if (speeddelay > 0) {
900 pfd[1].events &= ~POLLIN;
901 if (speeddelay < delay || delay < 0) delay=speeddelay;
906 n=poll(pfd,npfd,delay);
907 if (pfd[0].revents & POLLHUP || (ndirs>1 && pfd[1].revents & POLLHUP))
908 exit(0);
909 if (pfd[0].revents & POLLIN) {
910 packet_in(LR);
912 if (ndirs>1 && pfd[1].revents & POLLIN) {
913 packet_in(RL);
915 if (mgmtindex >= 0 && pfd[mgmtindex].revents != 0)
916 npfd=newmgmtconn(pfd[mgmtindex].fd,pfd,npfd);
917 if (npfd > mgmtindex+1) {
918 register int i;
919 for (i=mgmtindex+1;i<npfd;i++) {
920 if (pfd[i].revents & POLLHUP ||
921 (pfd[i].revents & POLLIN && mgmtcommand(pfd[i].fd) < 0))
922 npfd=delmgmtconn(i,pfd,npfd);
925 packet_dequeue();