split vpn into red and black modules
authorMazet Laurent <laurent.mazet@thalesgroup.com>
Thu, 12 Jun 2025 17:18:07 +0000 (19:18 +0200)
committerMazet Laurent <laurent.mazet@thalesgroup.com>
Thu, 12 Jun 2025 17:18:07 +0000 (19:18 +0200)
test-rb.sh [new file with mode: 0755]
ulvpn-rb.sh [new file with mode: 0755]
ulvpn.c

diff --git a/test-rb.sh b/test-rb.sh
new file mode 100755 (executable)
index 0000000..5c1fdb5
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+MODE="tx rx"
+MTU=1440
+#VPN=./ulvpn.exe
+MOREP=$(dirname $0)/../morep/morep_valid.exe
+
+MSG="TEST: This is a very long text"
+
+function mydata() { cat $1 | sed 's/.*=//'; }
+function title () { echo -e "\033[0;1m$*\033[0;0m"; }
+function pass () { echo -e "\033[1;32m$*\033[0;0m"; }
+function warn () { echo -e "\033[1;33m$*\033[0;0m"; test; }
+function fail () { echo -e "\033[1;31m$*\033[0;0m"; test; }
+ip addr list | grep 10\.1\.0\. || { ip link add eth0 type dummy && ip addr add dev eth0 10.1.0.1/24; }
+DEV=lo
+
+rm -f in.log out.log
+
+pids=
+{
+if [[ "$MODE" =~ tx ]]; then
+    stop=
+    while [ "x$stop" = "x" ]; do
+        echo -e "SLEEP 100\nR0839 MSG=0" | \
+            $MOREP -v 2 \
+                   -r $DEV://00:00:00:00:00:00/0839 \
+                   -t $DEV://00:00:00:00:00:00/0829 \
+                   -l out.log | \
+            awk '{print "CM R->B [rx]:", $0}';
+        grep -q 54:45:53:54 out.log && stop=1
+    done;
+
+    $MOREP -v 2 \
+       -r $DEV://00:00:00:00:00:00/0819 \
+       -t $DEV://00:00:00:00:00:00/0809 \
+       encrypted_data.eth | \
+       awk '{print "CM R->B [tx]:", $0}';
+fi
+} &
+pids="$pids $!"
+
+{
+if [[ "$MODE" =~ rx ]]; then
+    stop=
+    while [ "x$stop" = "x" ]; do
+        echo -e "SLEEP 100\nR0819 MSG=2" | \
+            $MOREP -v 2 \
+                   -r $DEV://00:00:00:00:00:00/0819 \
+                   -t $DEV://00:00:00:00:00:00/0809 \
+                   -l in.log | \
+            awk '{print "CM B->R [rx]:", $0}';
+        grep -q 54:45:53:54 in.log && stop=1
+    done;
+
+    $MOREP -v 2 \
+       -r $DEV://00:00:00:00:00:00/0839 \
+       -t $DEV://00:00:00:00:00:00/0829 \
+       clear_data.eth | \
+       awk '{print "CM B->R [tx]:", $0}';
+fi
+} &
+pids="$pids $!"
+
+{ sleep 1; echo $MSG | nc -uq0 10.2.1.1 3000; nc -luq0 3000 > text.log; } &
+pids="$pids $!"
+
+#trap "for p in $pids; do kill -9 $p; done; ip addr del dev eth0 10.1.0.1/24 && ip link del eth0 type dummy || echo no need to remove eth0;" 0 1 2 15
+
+$@ \
+  -v 5 -V 4 \
+  -d tun://tun0:$MTU \
+  -l tun://10.2.0.1 -r tun://10.2.1.0 \
+  -n udp \
+  -n black \
+  -y $DEV://00:00:00:00:00:00/0829 $DEV://00:00:00:00:00:00/0839 | \
+  awk '{ print "red:", $0 }' &
+pids="$pids $!"
+
+$@ \
+  -v 5 -V 4 \
+  -n tun \
+  -t udp://10.1.0.1:1234 -u udp://10.1.0.1:1234 \
+  -x $DEV://00:00:00:00:00:00/0809 $DEV://00:00:00:00:00:00/0819 \
+  -n red | \
+  awk '{ print "black:", $0 }' &
+pids="$pids $!"
+
+sleep 4
+
+for p in $pids; do
+    echo killing $p
+    pkill -TERM -P $p 2>/dev/null
+    kill -TERM $p 2>/dev/null
+done
+ip addr del dev eth0 10.1.0.1/24 && ip link del eth0 type dummy || echo no need to remove eth0
+
+rc=
+[[ "$MODE" =~ rx ]] && [ "$(mydata in.log)" = "$(mydata encrypted_data.eth)" ] && pass "encrypted data ok" || fail "encrypted data ko"
+rc="$rc$?"
+[[ "$MODE" =~ tx ]] && [ "$(mydata out.log|hd|tail -8)" = "$(mydata clear_data.eth|hd|tail -8)" ] && pass "clear data ok" || fail "clear data ko"
+rc="$rc$?"
+[ -f text.log ] && { echo $MSG | diff text.log - && pass "message ok" || fail "message ko"; }
+rc="$rc$?"
+
+test "$rc" = "000"
diff --git a/ulvpn-rb.sh b/ulvpn-rb.sh
new file mode 100755 (executable)
index 0000000..2eed51c
--- /dev/null
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+MTU=1440
+[ $# -gt 0 ] && MTU=$1
+
+LRED=lo://00:00:00:00:00:00
+LBLK=lo://00:00:00:00:00:00
+RRED=lo://00:00:00:00:00:00
+RBLK=lo://00:00:00:00:00:00
+
+CRYPTOMOD=./cryptomod.exe
+SIMULATOR=./simulator.exe
+VPN=./ulvpn.exe
+
+ulimit -c unlimited
+
+title() { echo -e "\033[0;1m$@\033[0;0m"; }
+
+cat > provisioning.eth <<EOF
+SLEEP 500
+T:PROVISIONING LOAD_KEY_REQ KEYID=0 KEY=01:02:03:04:05:06:07:08:09:0a:0b:0c:0d:0e:0f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20
+R:PROVISIONING LOAD_KEY_RESP STATUS=0
+EOF
+
+pids=
+if $(ip add list dev enp0s4 | fgrep -q 10.1.0.2/); then
+    title Remote
+
+    $CRYPTOMOD -v 1 -V 1 -l cryptomod.log \
+        -s BYPASS_B2R -t $RRED/080D -r $RBLK/083D \
+        -s BYPASS_R2B -t $RBLK/082D -r $RRED/081D \
+        -s CONTROL -t $RRED/080E -r $RRED/081E \
+        -s CROSS_CRYPTO_B2R -t $RRED/0809 -r $RBLK/0839 \
+        -s CROSS_CRYPTO_R2B -t $RBLK/0829 -r $RRED/0819 \
+        -s PROVISIONING -t $RRED/080B -r $RRED/081B &
+    pids="$pids $!"
+
+    $SIMULATOR -s PROVISIONING -r $RRED/080B -t $RRED/081B provisioning.eth | \
+        awk '{print "provisioning:", $0}'
+
+    $VPN -v 1 -V 1 -l ulvpn.log \
+        -d tun://tun0:$MTU \
+        -l tun://10.2.1.1 -r tun://10.2.0.0 \
+        -n udp \
+        -n black \
+        -y $RRED/0809 $RRED/0819 | \
+        awk '{print "red:", $0}' &
+    pids="$pids $!"
+
+    $VPN -v 1 -V 1 -l ulvpn.log \
+        -n tun \
+        -t udp://10.1.0.2:1234 -u udp://10.1.0.1:1234 \
+        -x $RBLK/0829 $RBLK/0839 \
+        -n red | \
+        awk '{print "black:", $0}'
+
+else
+    title Local
+
+    $CRYPTOMOD -v 1 -V 1 -l cryptomod.log \
+        -s BYPASS_B2R -t $LRED/080D -r $LBLK/083D \
+        -s BYPASS_R2B -t $LBLK/082D -r $LRED/081D \
+        -s CONTROL -t $LRED/080E -r $LRED/081E \
+        -s CROSS_CRYPTO_B2R -t $LRED/0809 -r $LBLK/0839 \
+        -s CROSS_CRYPTO_R2B -t $LBLK/0829 -r $LRED/0819 \
+        -s PROVISIONING -t $LRED/080B -r $LRED/081B &
+    pids="$pids $!"
+
+    $SIMULATOR -s PROVISIONING -r $RRED/080B -t $RRED/081B provisioning.eth | \
+        awk '{print "provisioning:", $0}'
+
+    $VPN -v 1 -V 1 -l ulvpn.log \
+        -d tun://tun0:$MTU \
+        -l tun://10.2.0.1 -r tun://10.2.1.0 \
+        -n udp \
+        -n black \
+        -y $LRED/0809 $LRED/0819 | \
+        awk '{print "red:", $0}' &
+    pids="$pids $!"
+
+    $VPN -v 1 -V 1 -l ulvpn.log \
+        -n tun \
+        -t udp://10.1.0.1:1234 -u udp://10.1.0.2:1234 \
+        -x $LBLK/0829 $LBLK/0839 \
+        -n red | \
+        awk '{print "black:", $0}'
+
+fi
+
+for p in $pids; do
+    echo killing $p
+    kill -TERM $p
+done
diff --git a/ulvpn.c b/ulvpn.c
index f6ca9e37081322c03d80f06002a4c07da9048838..62153419433926db644ae9ce1b5baef302758f93 100644 (file)
--- a/ulvpn.c
+++ b/ulvpn.c
@@ -39,10 +39,10 @@ char *loctun = "tun://10.2.0.1";
 char *remtun = "tun://10.2.1.0";
 char *locudp = "udp://10.1.0.1:1234";
 char *remudp = "udp://10.1.0.2:1235";
-char *cmlocblack = NULL;
-char *cmremblack = NULL;
-char *cmlocred = NULL;
-char *cmremred = NULL;
+char *cmlocblack = "lo://00:00:00:00:00:00/0829";
+char *cmremblack = "lo://00:00:00:00:00:00/0839";
+char *cmlocred = "lo://00:00:00:00:00:00/0809";
+char *cmremred = "lo://00:00:00:00:00:00/0819";
 
 int seqcheck = 0;
 int timeout = 900; /* ms */
@@ -70,10 +70,11 @@ void sig_handler (int sig)
 
 void usage (void)
 {
-    printf ("usage: %s [-d devtun] [-h] [-l loctun] [-r remtun] [-t locudp] [-u remudp] [-v int] [-x url url] [-y url url] [-w]\n", progname);
+    printf ("usage: %s [-d devtun] [-h] [-l loctun] [-n mod] [-r remtun] [-t locudp] [-u remudp] [-v int] [-x url url] [-y url url] [-w]\n", progname);
     printf (" -d: dev tun (%s)\n", devtun);
     printf (" -h: help message\n");
     printf (" -l: local tun (%s)\n", loctun);
+    printf (" -n: desactivate module (tun,udp,red,black)\n");
     printf (" -r: remote tun (%s)\n", remtun);
     printf (" -t: local udp (%s)\n", locudp);
     printf (" -u: remote udp (%s)\n", remudp);
@@ -248,6 +249,31 @@ int main (int argc, char **argv)
             }
             loctun = arg;
             break;
+        case 'n':
+            arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                VERBOSE (ulvpn, ERROR, PRINTF ("%s: module not specified\n", progname));
+                return 1;
+            }
+            if (strcmp (arg, "tun") == 0) {
+                devtun = NULL;
+                loctun = NULL;
+                remtun = NULL;
+            } else if (strcmp (arg, "udp") == 0) {
+                locudp = NULL;
+                remudp = NULL;
+            } else if (strcmp (arg, "black") == 0) {
+                cmlocblack = NULL;
+                cmremblack = NULL;
+            } else if (strcmp (arg, "red") == 0) {
+                cmlocred = NULL;
+                cmremred = NULL;
+            } else {
+                VERBOSE (ulvpn, ERROR, PRINTF ("%s: unknown module (%s)\n", progname, arg));
+                return 1;
+            }
+            break;
+
         case 'r':
             arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
             if (arg == NULL) {
@@ -317,13 +343,13 @@ int main (int argc, char **argv)
     }
 
     /* init udp and tun communication channels */
-    int tun_fd = MAPEC_Connect (devtun, loctun, remtun);
-    if (tun_fd < 0) {
+    int tun_fd = -1;
+    if (((devtun) && (loctun) && (remtun)) && ((tun_fd = MAPEC_Connect (devtun, loctun, remtun)) < 0)) {
         VERBOSE (ulvpn, ERROR, PRINTF ("can't open communication for %s %s %s\n", devtun, loctun, remtun));
         return -1;
     }
-    int udp_fd = MAPEC_Connect (locudp, remudp, NULL);
-    if (udp_fd < 0) {
+    int udp_fd = -1;
+    if (((locudp) && (remudp)) && ((udp_fd = MAPEC_Connect (locudp, remudp, NULL)) < 0)) {
         VERBOSE (ulvpn, ERROR, PRINTF ("can't open communication for %s %s\n", locudp, remudp));
         return -1;
     }
@@ -331,12 +357,20 @@ int main (int argc, char **argv)
     /* init crypto module communication channels */
     int rxblack_fd = (cmlocblack) ? MOREP_Connect (cmlocblack) : -1;
     int txblack_fd = (cmremblack) ? MOREP_Connect (cmremblack) : -1;
+    if ((rxblack_fd < 0) && (txblack_fd < 0)) {
+        VERBOSE (ulvpn, INFO, PRINTF ("CM Black desactivated\n"));
+    } else if ((rxblack_fd < 0) || (txblack_fd < 0)) {
+        VERBOSE (ulvpn, ERROR, PRINTF ("can't open communication for CM Black %s %s\n", cmlocblack, cmremblack));
+        return -1;
+    }
     int rxred_fd = (cmlocred) ? MOREP_Connect (cmlocred) : -1;
     int txred_fd = (cmremred) ? MOREP_Connect (cmremred) : -1;
-    int cmok = (rxblack_fd > -1) && (txblack_fd > -1) && (rxred_fd > -1) && (txred_fd > -1);
-    if (!cmok) {
-        VERBOSE (ulvpn, INFO, PRINTF ("CM desactivated\n"));
+    if ((rxred_fd < 0) && (txred_fd < 0)) {
+    } else if ((rxred_fd < 0) || (txred_fd < 0)) {
+        VERBOSE (ulvpn, ERROR, PRINTF ("can't open communication for CM red %s %s\n", cmlocred, cmremred));
+        return -1;
     }
+
     VERBOSE (ulvpn, INFO, PRINTF ("fids: %d %d %d %d %d %d\n", tun_fd, udp_fd, rxblack_fd, txblack_fd, rxred_fd, txred_fd));
 
     /* signals */
@@ -351,19 +385,25 @@ int main (int argc, char **argv)
 
         fd_set readset;
         int max_fd = 0;
-        FD_SET (tun_fd, &readset);
-        if (max_fd < tun_fd) {
-            max_fd = tun_fd;
+        if (tun_fd >= 0) {
+            FD_SET (tun_fd, &readset);
+            if (max_fd < tun_fd) {
+                max_fd = tun_fd;
+            }
         }
-        FD_SET (udp_fd, &readset);
-        if (max_fd < udp_fd) {
-            max_fd = udp_fd;
+        if (udp_fd >= 0) {
+            FD_SET (udp_fd, &readset);
+                if (max_fd < udp_fd) {
+                max_fd = udp_fd;
+            }
         }
-        if (cmok) {
+        if (rxblack_fd >= 0) {
             FD_SET (rxblack_fd, &readset);
             if (max_fd < rxblack_fd) {
                 max_fd = rxblack_fd;
             }
+        }
+        if (rxred_fd >= 0) {
             FD_SET (rxred_fd, &readset);
             if (max_fd < rxred_fd) {
                 max_fd = rxred_fd;
@@ -386,7 +426,7 @@ int main (int argc, char **argv)
            } else {
                 VERBOSE (ulvpn, DEBUG, PRINTF ("received %d bytes from tun\n", rxlen));
 
-                if (cmok) {
+                if (txred_fd >= 0) {
                     int txlen = pack_clear_data (buffer, rxlen, sizeof (buffer));
                     if (txlen <= 0) {
                         VERBOSE (ulvpn, WARNING, PRINTF ("error when packing clear data\n"));
@@ -399,13 +439,15 @@ int main (int argc, char **argv)
                             VERBOSE (ulvpn, DEBUG, PRINTF ("send %d bytes to CM red\n", txlen));
                         }
                     }
-                } else {
+                } else if (udp_fd >= 0) {
                     int txlen = MAPEC_Send (udp_fd, buffer, rxlen);
                     if (txlen <= 0) {
                         VERBOSE (ulvpn, WARNING, PRINTF ("error when sending to udp\n"));
                     } else {
                         VERBOSE (ulvpn, DEBUG, PRINTF ("sent %d bytes to udp\n", txlen));
                     }
+                } else {
+                    VERBOSE (ulvpn, WARNING, PRINTF ("can't handle message from tun\n"));
                 }
             }
 
@@ -417,7 +459,7 @@ int main (int argc, char **argv)
             } else {
                 VERBOSE (ulvpn, DEBUG, PRINTF ("received %d bytes from udp\n", rxlen));
 
-                if (cmok) {
+                if (txblack_fd >= 0) {
                     int txlen = pack_encrypted_data (buffer, rxlen, sizeof (buffer));
                     if (txlen <= 0) {
                         VERBOSE (ulvpn, WARNING, PRINTF ("error when packing encrypted data\n"));
@@ -430,13 +472,15 @@ int main (int argc, char **argv)
                             VERBOSE (ulvpn, DEBUG, PRINTF ("send %d bytes to CM black\n", txlen));
                         }
                     }
-                } else {
+                } else if (tun_fd >= 0) {
                     int txlen = MAPEC_Send (tun_fd, buffer, rxlen);
                     if (txlen <= 0) {
                         VERBOSE (ulvpn, WARNING, PRINTF ("error when sending to tun\n"));
                     } else {
                         VERBOSE (ulvpn, DEBUG, PRINTF ("sent %d bytes to tun\n", txlen));
                     }
+                } else {
+                    VERBOSE (ulvpn, WARNING, PRINTF ("can't handle message from udp\n"));
                 }
             }
 
@@ -455,22 +499,26 @@ int main (int argc, char **argv)
             } else {
                 VERBOSE (ulvpn, DEBUG, PRINTF ("received %d bytes from CM black\n", rxlen));
 
-                if (seqtx != seqrx) {
+                if ((rxred_fd >= 0) && (seqtx != seqrx)) {
                     VERBOSE (ulvpn, WARNING, PRINTF ("incoherent seqnum (%d/%d)\n", seqtx, seqrx));
                 }
 
                 if (msgtype == 0x01) {
-                    int txlen = depack_encrypted_data (buffer, rxlen, sizeof (buffer));
-                    if (txlen <= 0) {
-                        VERBOSE (ulvpn, WARNING, PRINTF ("error when depacking encrypted data\n"));
-                    } else {
-                        VERBOSE (ulvpn, DEBUG, PRINTF ("depacking %d bytes\n", txlen));
-                        txlen = MAPEC_Send (udp_fd, buffer, txlen);
+                    if (udp_fd >= 0) {
+                        int txlen = depack_encrypted_data (buffer, rxlen, sizeof (buffer));
                         if (txlen <= 0) {
-                            VERBOSE (ulvpn, WARNING, PRINTF ("error when sending to udp\n"));
+                            VERBOSE (ulvpn, WARNING, PRINTF ("error when depacking encrypted data\n"));
                         } else {
-                            VERBOSE (ulvpn, DEBUG, PRINTF ("sent to udp %d bytes\n", txlen));
-                        }
+                            VERBOSE (ulvpn, DEBUG, PRINTF ("depacking %d bytes\n", txlen));
+                            txlen = MAPEC_Send (udp_fd, buffer, txlen);
+                            if (txlen <= 0) {
+                                VERBOSE (ulvpn, WARNING, PRINTF ("error when sending to udp\n"));
+                            } else {
+                                VERBOSE (ulvpn, DEBUG, PRINTF ("sent to udp %d bytes\n", txlen));
+                            }
+                         }
+                    } else {
+                        VERBOSE (ulvpn, WARNING, PRINTF ("can't handle message %d from CM black\n", msgtype));
                     }
 
 
@@ -494,22 +542,26 @@ int main (int argc, char **argv)
             } else {
                 VERBOSE (ulvpn, DEBUG, PRINTF ("received %d bytes from CM red\n", rxlen));
 
-                if (seqtx != seqrx) {
+                if ((rxblack_fd >= 0) && (seqtx != seqrx)) {
                     VERBOSE (ulvpn, WARNING, PRINTF ("incoherent seqnum (%d/%d)\n", seqtx, seqrx));
                 }
 
                 if (msgtype == 0x03) {
-                    int txlen = depack_clear_data (buffer, rxlen, sizeof (buffer));
-                    if (txlen <= 0) {
-                        VERBOSE (ulvpn, WARNING, PRINTF ("error when depacking clear data\n"));
-                    } else {
-                        VERBOSE (ulvpn, DEBUG, PRINTF ("depacking %d bytes\n", txlen));
-                        txlen = MAPEC_Send (tun_fd, buffer, txlen);
+                    if (tun_fd >= 0) {
+                        int txlen = depack_clear_data (buffer, rxlen, sizeof (buffer));
                         if (txlen <= 0) {
-                            VERBOSE (ulvpn, WARNING, PRINTF ("error when sending to tun\n"));
-                        } else {
-                            VERBOSE (ulvpn, DEBUG, PRINTF ("sent to tun %d bytes\n", txlen));
+                            VERBOSE (ulvpn, WARNING, PRINTF ("error when depacking clear data\n"));
+                            } else {
+                            VERBOSE (ulvpn, DEBUG, PRINTF ("depacking %d bytes\n", txlen));
+                            txlen = MAPEC_Send (tun_fd, buffer, txlen);
+                            if (txlen <= 0) {
+                                VERBOSE (ulvpn, WARNING, PRINTF ("error when sending to tun\n"));
+                            } else {
+                                VERBOSE (ulvpn, DEBUG, PRINTF ("sent to tun %d bytes\n", txlen));
+                            }
                         }
+                    } else {
+                        VERBOSE (ulvpn, WARNING, PRINTF ("can't handle message %d from CM red\n", msgtype));
                     }
 
 
@@ -543,6 +595,13 @@ int main (int argc, char **argv)
 /* test: ulvpn.exe -x 2>&1 | grep 'black cm service not specified' */
 /* test: ulvpn.exe -y 2>&1 | grep 'red cm service not specified' */
 
+/* test: ulvpn.exe -n 2>&1 | grep 'module not specified' */
+/* test: ulvpn.exe -n foo 2>&1 | grep 'unknown module' */
+/* test: ulvpn.exe -n tun -h | grep 'dev tun ((null))' */
+/* test: ulvpn.exe -n udp -h | grep 'local udp ((null))' */
+/* test: ulvpn.exe -n black -h | grep 'black CM service ((null)|(null))' */
+/* test: ulvpn.exe -n red -h | grep 'red CM service ((null)|(null))' */
+
 /* test: sudo -u `awk -F: '/sh$/ && $3 != 0 {print $1; exit}' /etc/passwd` ulvpn.exe; test $? -ne 0 */
 
 /* test: ulvpn.exe -d tun://tun0:-1; test $? -ne 0 */
@@ -562,14 +621,15 @@ int main (int argc, char **argv)
 /* test: ulvpn.exe -u udp://nohost.nowhere:1235; test $? -ne 0 */
 /* test: ulvpn.exe -u udp://localhost:-1; test $? -ne 0 */
 
-/* test: ulvpn.exe -v 5 -V 5 & pid=$!; sleep 1; ip route list dev tun0; rc=$?; sleep 1; kill -INT $pid; test $rc -eq 0 */
+/* test: ulvpn.exe -v 5 -V 5 & pid=$!; sleep 1; ip route list dev tun0; rc=$?; kill -INT $pid; sleep 1; test $rc -eq 0 */
 
-/* test: ulvpn.exe -v 5 -t udp://\*:1234 -u udp://localhost:1235 & pid1=$!; sleep 1; nc -ul 1235 > pcap & pid2=$!; sleep 1; echo TEST | nc -Nuq0 10.2.1.1 3000; sleep 1; kill -TERM $pid1 $pid2; strings pcap | grep -q TEST */
-/* test: ulvpn.exe -v 5 -l tun://10.2.1.1 -r tun://10.2.0.0 -t udp://localhost:1234 -u udp://localhost:1235 & pid1=$!; sleep 1; nc -ul 3000 > text & pid2=$!; sleep 1; cat pcap | nc -4Nuq0 -p 1235 localhost 1234; sleep 1; kill -TERM $pid1 $pid2; test "$(cat text)" = TEST */
+/* test: ulvpn.exe -v 5 -n black -n red -t udp://\*:1234 -u udp://localhost:1235 & pid1=$!; sleep 1; nc -ul 1235 > pcap & pid2=$!; sleep 1; echo TEST | nc -Nuq0 10.2.1.1 3000; sleep 1; kill -TERM $pid1 $pid2; strings pcap | grep -q TEST */
+/* test: ulvpn.exe -v 5 -n black -n red -l tun://10.2.1.1 -r tun://10.2.0.0 -t udp://localhost:1234 -u udp://localhost:1235 & pid1=$!; sleep 1; nc -ul 3000 > text & pid2=$!; sleep 1; cat pcap | nc -4Nuq0 -p 1235 localhost 1234; sleep 1; kill -TERM $pid1 $pid2; test "$(cat text)" = TEST */
 /* test: rm -f pcap text */
 
 /* test: ip addr del dev eth0 10.1.0.1/24 && ip link del eth0 type dummy || echo no need to remove eth0 */
 
 /* test: ./test.sh ulvpn.exe */
+/* test: ./test-rb.sh ulvpn.exe */
 
 /* vim: set ts=4 sw=4 si et: */