#include <u.h>
#include <libc.h>
#include <ctype.h>

int     debug;
long    errrate;
long    droprate;
int     framing;
int     nocompress;
int     noipcompress;
char    *ppp = "8.out";
char    *mtu;

void
pppopen(int fd, char *net, char *local, char *remote)
{
       char *argv[16];
       int argc;

       switch(fork()){
       case -1:
               fprint(2, "testppp: can't fork: %r\n");
               exits(0);
       case 0:
               return;
       default:
               break;
       }

       dup(fd, 0);
       dup(fd, 1);

       argc = 0;
       argv[argc++] = ppp;
       if(debug)
               argv[argc++] = "-d";
       if(framing)
               argv[argc++] = "-f";
       if(nocompress)
               argv[argc++] = "-c";
       if(noipcompress)
               argv[argc++] = "-C";
       if(mtu){
               argv[argc++] = "-m";
               argv[argc++] = mtu;
       }
       argv[argc++] = "-x";
       argv[argc++] = net;
       if(local){
               argv[argc++] = local;
               if(remote)
                       argv[argc++] = remote;
       }
       argv[argc] = 0;
       exec(ppp, argv);
}

void
printbuf(uchar *p, int n)
{
       int i;
       uchar *e;
       char buf[32*5];

       if(n > 32)
               n = 32;

       i = 0;
       for(e = p + n; p < e; p++){
               if(isprint(*p))
                       i += sprint(buf+i, "%c ", *p);
               else
                       i += sprint(buf+i, "%2.2ux ", *p);
       }
       fprint(2, "%s\n", buf);
}

void
xfer(int from, int to)
{
       uchar buf[4096];
       int i, n, modified, ok, total, errs, dropped;

       if(fork() == 0)
               return;

       total = ok = errs = dropped = 0;
       for(;;){
               n = read(from, buf, sizeof(buf));
               if(n <= 0){
                       fprint(2, "%d -> %d EOF\n", from, to);
                       exits(0);
               }
               modified = 0;
               if(errrate){
                       for(i = 0; i < n; i++){
                               if(lnrand(errrate) == 0){
                                       buf[i] ^= 0xff;
                                       modified = 1;
                               }
                       }
               }
               if(droprate && lnrand(droprate) == 0){
                       fprint(2, "!!!!!!!!!!!!!!%d -> %d dropped %d (%d/%d)\n", from, to, ok, dropped, total);
                       ok = 0;
                       dropped++;
                       total++;
                       continue;
               }
               if(modified){
                       fprint(2, "!!!!!!!!!!!!!!%d -> %d %d (%d/%d)\n", from, to, ok, errs, total);
                       ok = 0;
                       errs++;
               } else
                       ok++;
               total++;
               if(debug > 1){
                       fprint(2, "%d -> %d (%d)", from, to, n);
                       printbuf(buf, n);
               }
               n = write(to, buf, n);
               if(n < 0){
                       fprint(2, "%d -> %d write err\n", from, to);
                       exits(0);
               }
       }
}

void
usage(void)
{
       fprint(2, "usage: testppp [-cCDf] [-e errrate] [-d droprate] [-m mtu] [-p ppp]\n");
       exits("usage");
}

void
main(int argc, char **argv)
{
       char *s;
       int pfd1[2];
       int pfd2[2];

       errrate = 0;
       droprate = 0;
       ARGBEGIN{
       case 'c':
               nocompress = 1;
               break;
       case 'C':
               noipcompress = 1;
               break;
       case 'd':
               s = ARGF();
               if(s)
                       droprate = strtol(s, nil, 0);
               break;
       case 'D':
               debug++;
               break;
       case 'e':
               s = ARGF();
               if(s)
                       errrate = strtol(s, nil, 0);
               break;
       case 'f':
               framing = 1;
               break;
       case 'm':
               mtu = ARGF();
               break;
       case 'p':
               ppp = ARGF();
               if(ppp == nil)
                       usage();
               break;
       default:
               usage();
               break;
       }ARGEND
       if(argc)
               usage();

       pipe(pfd1);
       pipe(pfd2);

       bind("#I2", "/net.alt2", MCREATE);
       bind("#I1", "/net.alt", MCREATE);
       pppopen(pfd1[0], "/net.alt2", "135.104.99.1", "135.104.99.2");
       pppopen(pfd2[0], "/net.alt", 0, 0);

       close(pfd1[0]);
       close(pfd2[0]);

       xfer(pfd1[1], pfd2[1]);
       xfer(pfd2[1], pfd1[1]);
       exits(0);
}