Claudio, Thanks for this, I compiled it on Openbsd 6.6 (stable) amd64 it compiled without error
the binary seems to run fine but, ./tbridge -k /dev/tap0 /dev/tap1 runs and displays the usage message and gives an errorlevel of 1 every time use the -k or -t or -s or -p arguments see terminal conversation below test3b# ./tbridge -k /dev/tap0 /dev/tap1 tbridge -k | -p | -s | -t tapA tapB persistentg3b# ./tbridge -p /dev/tap0 /dev/tap1 tbridge -k | -p | -s | -t tapA tapB test3b# echo $? 1 test3b# ./tbridge -s /dev/tap0 /dev/tap1 tbridge -k | -p | -s | -t tapA tapB test3b# echo $? 1 test3b# ./tbridge -t /dev/tap0 /dev/tap1 tbridge -k | -p | -s | -t tapA tapB test3b# echo $? 1 test3b# ./tbridge /dev/tap0 /dev/tap1 test3b# echo $? 0 I tried with and without creating the tunnel interfaces first, with ifconfig tap create i tried with our without running ifconfig tap1 up i tried with and without adding each tap to a separate bridge(4) I ran the binary as root for all tests I tried running tbridge with interface name "tap1" / "tap0" as opposed to the device name /dev/tap1 /dev/tap2 (just in case) will try with current after I get some sleep ( was just trying to do a benchmark of release /stable vs current also ) Thanks for this it is a help as I was trying and (losing with socat) I think socat port on OpenBSD6.6 amd64 is compiled without tap / tun support cheers, Tom Smyth On Mon, 20 Jan 2020 at 10:38, Claudio Jeker <cje...@diehard.n-r-g.com> wrote: > > On Fri, Jan 10, 2020 at 01:00:49PM +0000, Tom Smyth wrote: > > Hi lads, > > > > I have been doing some testing with tap(4) and openvpn (standard ssl ) > > I have been using openvpn with tap and I have been trying with null > > encryption. null authentication, > > the performance of the tap interface seems to be about 100-150Mb/s on a > > system > > which can give 3Gb/s-5Gb/s on ix(4) interfaces in Bridge mode and > > 4-8Gb/s on tpmr mode > > I was wondering is there a sysctl setting that if modified would > > improve the tap interface performance. > > I have tried with tpmr(4) and bridge(4) > > > > is there a simple way testing a tap(4) interface throughput / > > performance without Openvpn process > > > > I can try mlvpn and wireguard > > but I would love if there was a trick where I can just test the tap(4) > > interface with something like pair(4)... > > > > ix0---bridge0--tap0---someprocess--tap1-bridge1--ix1 > > or > > ix0--tpmr0--tap0--someprocess--tap1-tpmr1-ix1 > > > > is there a simple "someprocess" that would provide forwarding packets > > between tap0 and tap1 in userland > > so that any performance testing on tap(4) interfaces does not have the > > distractions of complex userland programs with encryption / > > encapsulation overheads > > > > I just wrote a simple tun/tap bridge for testing so here you go. > Compile it with 'cc -Wall -o tbridge tbridge.c -lpthread' and run it > with 'tbridge -k /dev/tun0 /dev/tun1' to wire tun0 and tun1 together. > You can select between, select(2), poll(2), kqueue(2) and pthreads as the > way on how to multiplex the reads. > > For me the code triggers scheduler inefficencies and causes packets drops > on the output queue when there are multiple packet producers. > -- > :wq Claudio > > /* > * Copyright (c) 2020 Claudio Jeker <clau...@openbsd.org> > * > * Permission to use, copy, modify, and distribute this software for any > * purpose with or without fee is hereby granted, provided that the above > * copyright notice and this permission notice appear in all copies. > * > * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > */ > #include <sys/types.h> > #include <sys/event.h> > #include <sys/time.h> > > #include <err.h> > #include <errno.h> > #include <fcntl.h> > #include <poll.h> > #include <pthread.h> > #include <signal.h> > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > #include <unistd.h> > > volatile sig_atomic_t quit; > > static void > do_read(int in, int out) > { > char buf[2048]; > ssize_t n, o; > > n = read(in, buf, sizeof(buf)); > if (n == -1) > err(1, "read"); > o = write(out, buf, n); > if (o == -1) > err(1, "read"); > if (o != n) > errx(1, "short write"); > } > > static void > do_poll(int fd[2]) > { > struct pollfd pfd[2]; > int n, i; > > while (quit == 0) { > memset(pfd, 0, sizeof(pfd)); > pfd[0].fd = fd[0]; > pfd[0].events = POLLIN; > > pfd[1].fd = fd[1]; > pfd[1].events = POLLIN; > > n = poll(pfd, 2, INFTIM); > if (n == -1) > err(1, "poll"); > if (n == 0) > errx(1, "poll: timeout"); > for (i = 0; i < 2; i++) { > if (pfd[i].revents & POLLIN) > do_read(fd[i], fd[(i + 1) & 0x1]); > else if (pfd[i].revents & (POLLHUP | POLLERR)) > errx(1, "fd %d revents %x", i, > pfd[i].revents); > } > } > > } > > static void > do_select(int fd[2]) > { > fd_set readfds; > int n, i, maxfd = -1; > > while (quit == 0) { > FD_ZERO(&readfds); > for (i = 0; i < 2; i++) { > if (fd[i] > maxfd) > maxfd = fd[i]; > FD_SET(fd[i], &readfds); > } > n = select(maxfd + 1, &readfds, NULL, NULL, NULL); > if (n == -1) > err(1, "select"); > if (n == 0) > errx(1, "select: timeout"); > for (i = 0; i < 2; i++) { > if (FD_ISSET(fd[i], &readfds)) > do_read(fd[i], fd[(i + 1) & 0x1]); > } > } > } > > static void > do_kqueue(int fd[2]) > { > struct kevent kev[2]; > int kq, i, n; > > if ((kq = kqueue()) == -1) > err(1, "kqueue"); > > memset(kev, 0, sizeof(kev)); > for (i = 0; i < 2; i++) { > EV_SET(&kev[i], fd[i], EVFILT_READ, EV_ADD | EV_ENABLE, > 0, 0, (void *)(intptr_t)i); > } > if (kevent(kq, kev, 2, NULL, 0, NULL) == -1) > err(1, "kevent register"); > > while (quit == 0) { > n = kevent(kq, NULL, 0, kev, 2, NULL); > if (n == -1) > err(1, "kevent"); > if (n == 0) > errx(1, "kevent: timeout"); > for (i = 0; i < n; i++) { > if (kev[i].flags & EV_ERROR) > errc(1, kev[i].data, "kevent EV_ERROR"); > if (kev[i].filter == EVFILT_READ) { > int r = (int)kev[i].udata; > do_read(fd[r], fd[(r + 1) & 0x1]); > } > } > } > } > > static void * > run_thread(void *arg) > { > int *fd = arg; > > while (quit == 0) > do_read(fd[0], fd[1]); > > return NULL; > } > > static void > do_thread(int fd[2]) > { > pthread_t tid; > int ret; > > ret = pthread_create(&tid, NULL, run_thread, fd); > if (ret) { > errc(1, ret, "pthread_create"); > } > > while (quit == 0) > do_read(fd[1], fd[0]); > } > > static void > sighdlr(int sig) > { > quit = 1; > } > > static __dead void > usage(void) > { > fprintf(stderr, "tbridge -k | -p | -s | -t tapA tapB\n"); > exit(1); > } > > int > main(int argc, char **argv) > { > int fd[2]; > int ch = 0; > int mode = 0; > > while ((ch = getopt(argc, argv, "kpst")) != -1) { > switch (ch) { > case 'k': > case 'p': > case 's': > case 't': > if (ch != 0) > usage(); > mode = ch; > break; > default: > usage(); > } > } > argc -= optind; > argv += optind; > if (argc != 2) > usage(); > > signal(SIGTERM, sighdlr); > signal(SIGINT, sighdlr); > signal(SIGHUP, sighdlr); > > fd[0] = open(argv[0], O_RDWR); > if (fd[0] == -1) > err(1, "open %s", argv[1]); > > fd[1] = open(argv[1], O_RDWR); > if (fd[1] == -1) > err(1, "open %s", argv[2]); > > switch (mode) { > case 'k': > do_kqueue(fd); > break; > case 'p': > do_poll(fd); > break; > case 's': > do_select(fd); > break; > case 't': > do_thread(fd); > break; > } > exit(0); > } -- Kindest regards, Tom Smyth.