.

DD-WRT and OpenVPN (without nvram footprint)

networking-ddwrt-and-openvpnIn a virtual private network (VPN) a group of two or more computer systems communicate securely over the public internet.  Security is provided by authentication and encryption.  Computers that connect through a VPN have access to private network as if they were physically connected to it.

This article focuses on the OpenVPN implementation as it is included in the DD-Wrt router firmware.  Besides security, one of its prominent features is that its traffic can traverse network address translators (NAT) and firewalls.

Two examples are given that describe the typical configurations:

  • remote access, providing remote users access to a private network, and
  • site-to-site, connecting a remote network to a private network.

These examples have been tested on two Asus RT-N16 routers running DD-Wrt mega build 15943.  The approach outlined here might apply to other routers or builds that support OpenVPN and a writable file system such as JFFS, albeit with minor changes.

Before you start

This article assumes that readers possess a prior understanding of basic networking concepts such as IP addresses, DNS names, sub nets, routing, bridging, network interfaces, LANs and firewall rules.

Prior to configuring OpenVPN, we need to gain shell access to the routers, give the server a domain name, and create writable storage, keys and certificates.

Writable storage

The most obvious method of configuring OpenVPN is through the DD-Wrt web interface (WebGUI).  This stores the configuration, certificates and keys in nonvolatile memory (nvram).  In many routers, there may not be enough nvram to store all this information.  To show the amount of nvram available, use the command “nvram show | grep size:”.

The approach presented here, has no nvram footprint.  Instead it stores the configuration, keys and certificates on an USB memory stick.  To format this memory stick, use the WebGUI as described in the DD-Wrt JFFS Wiki.  An excerpt:

  1. Insert the memory stick into any available USB port on the back of your DD-Wrt router.
  2. In the WebGUI:
    • Administration > JFFS2 Support > Enable = true, click Save
    • Wait a few seconds, the click Apply, wait a few seconds
    • Administration > JFFS2 Support > Clean JFFS = true, click Apply
    • Wait for the router to finish formatting the memory stick and brings up the WebGUI again.
    • Administration > JFFS2 Support > Clean JFFS = true, click Save
  3. Reboot the router (just to make sure)
  4. The memory stick will auto mount at /jffs.

Certificates and keys

OpenVPN uses bidirectional authentication with X.509 certificates.  In this model the Certifying Authority (CA) is a third party that is trusted by the owner of the certificate and the party verifying the certificate.

To establish  mutual trust, the client authenticates the server certificate, and the server authenticates the client certificate.  It first ensures that the presented certificate was signed by a certifying authority (CA) , and verifies the common name (CN) and certificate type (client or server).  For details refer to the OpenVPN Howto.

In the examples below, we will setup our own CA and generate self signed certificates using a Linux host.  The procedure of generating the certificates is simular for the Windows version of OpenVPN.

  1. Copy the easy-rsa directory and initialize:
    cp -R /usr/share/openvpn/easy-rsa/2.0 ~/openvpn-ca  # easy-rsa may be elsewhere
    cd ~/openvpn-ca
    vi vars
      # update the fields at the end of the file
      # change KEY_SIZE value to 2048
    vi openssl-1.0.0.cnf
      # change default_md value to sha512
    vi pkitool
      # change the two occurrences of -sha1 to -sha512
    . ./vars
    ./clean-all
  2. While we’re at it, assign extended key usage that will allow you to use the client certificates for other purposes such as code signing and email.
    vi .`whichopensslcnf`  # open openssl-1.0.0.cnf with Notepad under Windows
    # under [ usr_cert ]
    #  extendedKeyUsage=clientAuth, emailProtection, codeSigning
    #  keyUsage=digitalSignature

    later if so desired, you can convert client certificates to PKCS12 format usi

  3. (optional) To use the key pair on Windows, you need to convert it to PKCS12 format
    openssl pkcs12 -export -in keys/client.crt -inkey keys/client.key \
               -out keys/client.p12 -certfile keys/ca.crt

    You can then import the certificates in windows by running “certmgr.msc” and importing the ca.crt to the Trusted Root Certificate Authority, and the client.p12 to the Personal certificates.

  4. (optional) To use the key pair for SSH or SSH2
    ssh-keygen -y -f keys/username.key > keys/username_openssh.pub
    ssh-keygen -e -f keys/username_openssh.pub > keys/username_ssh2.pub
    openssl rsa -in keys/username.key > keys/username_ssh2.key

    The SSH2 keypair (username_ssh2.key) can then be imported in e.g. Bitvise SSH.

  5. (optional) To use the key pair for Java signing, from a cmd prompt in Windows
    set NAME=changeme
    set PASSWD_OUT=changeme
    openssl pkcs12 -in keys\%NAME%.p12 -out keys\temp.pem -passin pass: -passout pass:%PASSWD_OUT%
    openssl pkcs12 -export -in keys\temp.pem -out keys\%NAME%_passwd.p12 -passin pass:%PASSWD_OUT% -passout pass:%PASSWD_OUT%
    keytool -importkeystore -srckeystore keys\%NAME%_passwd.p12 -destkeystore keys\%NAME%.jks -srcstoretype pkcs12 -srcstorepass %PASSWD_OUT% -deststorepass %PASSWD_OUT%
    del keys\temp.pem

    The JKS keypair can then be imported in e.g. Android Studio.

  6. Create a certifying authority certificate and corresponding secret key.  Use your name followed by CA as the common name:
    ./build-ca
  7. Create Diffie Hellman parameters and shared secret key for tls-auth:
    ./build-dh
    openvpn --genkey --secret keys/ta.key
  8. Show the certificate details:
    openssl x509 -in keys/ca.crt -noout -text

Real-time clock

Certificates have introduction and expiration dates, and therefore require the real-time clock and time zone to be set correctly.

The Network Time Protocol (NTP) can be used to automatically set the clocks.  To configure NTP on the DD-Wrt server and clients:

  • Setup > Basic Setup > Time Settings.  For my locality the settings are:
    • Time Zone = UTC-08:00
    • Summer Time = 2nd Sun Mar – first Sun Nov
    • Server IP/Name = 0.north-america.pool.ntp.org

Domain name

Usually the client will find the server by its public DNS name.  If your router gets a dynamic WAN address, you should configure a Dynamic DNS (DDNS) service on the router used as OpenVPN server.  This will give the router a public domain name whose address is automatically updated.

  • Setup > DDNS > Dynamic Domain Name System (my personal favorite is afraid.org)
    • DDNS Service = freedns.afraid.org
    • Host Name = yourhostname,hash (the hash value can be obtained as described in DD-Wrt and freedns.afraid.org).

SSH and emacs

The examples below assume that routers are accessible using secure shell (ssh).  This also enable ssh features in an editor such as emacs. For example, on the Linux host used as the certifying authority:

emacs /rtr:/jffs/etc/config/rtr-server.wanup

Where rtr corresponds to an entry in  your ~/.ssh/config file.

Routing versus Bridging

OpenVPN can connect clients using network routing or bridging.  For a comparison between routing and bridging, refer to Bridging vs. Routing.  An excerpt:

  • Routing uses the kernel device TUN to simulate a network layer device.  It operates at the network layer where it handles IP packets.  As a result, it:
    • has a low overhead, because it does not support broadcasts or require Ethernet headers;
    • for IPv6, OpenVPN version >=2.3 is required.
    • devices such as Android only support TUN.
  • Bridging uses the kernel device TAP to simulates a link layer device.  It operates at the link layer and handles Ethernet frames.  Consequently it:
    • supports non-IP based traffic;
    • supports Windows network neighborhood discovery without using WINS;
    • broadcasts and Ethernet headers cause overhead (for examples, you need to tell DHCP server not to respond to request coming over TAP).

Routing example: Site-to-Site

OpenVPN can be used to connect two local area networks (LANs).  In this example, two DD-Wrt routers are used.  One serves as the server, and the other as the client.  Note that the private network numbers are fairly arbitrary.  They were chosen to match my existing networks.

  • rtr, the server, with
    • WAN address rtr.yourdomain.com
    • LAN network 10.0.1.0/24
  • rtr2, the client.
    • LAN network 10.0.2.0/24

The server will use network 10.0.3.0/24 for the VPN.  Of that allocates the first address (10.0.3.1) for itself.  Clients will be allocated one of the remaining addresses.

rtr-server

The examples presented here, do not use the WebGUI nor NVRAM.  Instead they use bash scripts store on an memory stick and mounted as /jffs.  The scripts are automatically started by DD-Wrt each time the WAN link and firewall are up.  For more information refer to DD-Wrt Script Execution.

Certificates

  • On the Linux host, create certificates and corresponding secret keys for the server and client.
    ./build-key-server rtr-server # common name = rtr-server, no passwd
    ./build-key        rtr2       # common name = rtr2, no passwd

OpenVPN Server

  • From the Linux host, copy the certificates/keys files to /jffs/etc/openvpn on the server.
    ssh rtr mkdir -p /jffs/etc/config
    ssh rtr mkdir -p /jffs/etc/openvpn/ccd
    scp ca.crt dh2048.pem ta.key rtr-server.crt rtr-server.key rtr:/jffs/etc/openvpn/
  • Create a startup script /jffs/etc/openvpn/config/rtr-server.wanup on the server.  Make the script executable (chmod 755).  DD-Wrt will call this script each time the WAN link comes up.
    #!/bin/sh
    
    DEV=tun0
    PORT=1194
    PROTO=udp
    
    BASE=$(basename $0)
    NAME=${BASE%\.*}
    TMP=/tmp/$NAME
    PID=$TMP/pid
    LOG=$TMP/log
    #LOG=/dev/null
    DIR=$(dirname $0)/../openvpn
    
    [ -d $TMP ] || mkdir -p $TMP
    
    if [ -e $PID ] ; then
        kill -0 `cat $PID`
        if [ $? -eq 1 ] ; then
    	rm $PID  # remove false PID
        fi
    fi
    
    if [ -e $PID ] ; then
        kill -HUP `cat $PID`  # restart
    else
        rm $PID
        ln -s /usr/sbin/openvpn $TMP/openvpn
        $TMP/openvpn \
    	--daemon --writepid $PID --log-append $LOG \
    	--cd $DIR --config $NAME.ovpn \
    	--dev $DEV --port $PORT --proto $PROTO \
    	--up "$DIR/$NAME.helper $DEV $PORT $PROTO up" \
    	--down "$DIR/$NAME.helper $DEV $PORT $PROTO down" \
    	--ifconfig-pool-persist $TMP/ip-pool 86400
    fi
  • Create an helper script /jffs/etc/openvpn/rtr-server.helper on the server. Make the script executable (chmod 755).
    #!/bin/sh
    DEV=$1
    PORT=$2
    PROTO=$3
    case "$4" in
        'up')
            iptables -I INPUT -p $PROTO --dport $PORT -j ACCEPT
            iptables -I INPUT   -i $DEV -j ACCEPT
            iptables -I FORWARD -i $DEV -j ACCEPT
            iptables -I FORWARD -o $DEV -j ACCEPT
    	;;
        'down')
            iptables -D INPUT -i $DEV -j ACCEPT
            iptables -D INPUT -p $PROTO --dport $PORT -j ACCEPT
            iptables -D FORWARD -i $DEV -j ACCEPT
            iptables -D FORWARD -o $DEV -j ACCEPT
    	;;
        *)
    	exit 1
    esac
    exit 0
  • Create the configuration file /jffs/etc/openvpn/rtr-server.ovpn on the server
    server 10.0.3.0 255.255.255.0
    ca ca.crt
    cert rtr-server.crt
    key rtr-server.key
    dh dh2048.pem
    tls-auth ta.key 0
    tls-server
    keepalive 10 120
    cipher bf-cbc
    auth sha1
    mtu-disc yes
    topology subnet
    fast-io
    verb 4
    mute 5
    management 127.0.0.1 5002
    management-log-cache 50
    push "route 10.0.1.0 255.255.255.0"
    client-config-dir ccd                  # run client script matching CN
    route 10.0.2.0 255.255.255.0 10.0.3.1  # kernel to OpenVPN server
    client-to-client
  • Create the client specific configuration file /jffs/etc/openvpn/ccd/rtr2 on the server
    iroute 10.0.2.0 255.255.255.0  # OpenVPN server to remote clients
  • Start the OpenVPN server
    /jffs/etc/config/rtr-server.wanup
  • See how it is going.  Note that you can also use the WebGUI (Status > OpenVPN) to inspect the OpenVPN log messages.
    more /tmp/rtr-server/log  # a treasure trove of debug information
    netstat -lu               # should be listening on port 1194
    iptables -nvL             # INPUT chain should have port 1194 open

OpenVPN Client

  • From the Linux host, copy the certificates/keys files to /jffs/etc/openvpn
    ssh rtr2 mkdir -p /jffs/etc/config
    ssh rtr2 mkdir -p /jffs/etc/openvpn
    scp ca.crt ta.key rtr2.crt rtr2.key rtr2:/jffs/etc/openvpn/
  • Create a startup script /jffs/etc/openvpn/config/rtr2-to-rtr.wanup on the server.  Remember to make the script executable (chmod 755).  DD-Wrt will call this script each time the WAN link comes up.
    #!/bin/sh
    
    DEV=tun0
    PORT=1194
    PROTO=udp
    
    BASE=$(basename $0)
    NAME=${BASE%\.*}
    TMP=/tmp/$NAME
    PID=$TMP/pid
    LOG=$TMP/log
    #LOG=/dev/null
    DIR=$(dirname $0)/../openvpn
    
    [ -d $TMP ] || mkdir -p $TMP
    
    if [ -e $PID ] ; then
        kill -0 `cat $PID`
        if [ $? -eq 1 ] ; then
    	rm $PID  # remove false PID
        fi
    fi
    
    if [ -e $PID ] ; then
        kill -HUP `cat $PID`  # restart
    else
        rm $PID
        ln -s /usr/sbin/openvpn $TMP/openvpn
        $TMP/openvpn \
    	--daemon --writepid $PID --log-append $LOG \
    	--cd $DIR --config $NAME.ovpn \
    	--dev $DEV --port $PORT --proto $PROTO \
    	--up "$DIR/$NAME.helper $DEV up" \
    	--down "$DIR/$NAME.helper $DEV down"
    fi
  • Create an helper script /jffs/etc/openvpn/rtr2-to-rtr.helper on the server. Remember to make the script executable (chmod 755).
    #!/bin/sh
    DEV=$1
    case "$2" in
        'up')
            iptables -I INPUT   -i $DEV -j ACCEPT
            iptables -I FORWARD -i $DEV -j ACCEPT
            iptables -I FORWARD -o $DEV -j ACCEPT
    	;;
        'down')
            iptables -D INPUT -i $DEV -j ACCEPT
            iptables -D FORWARD -i $DEV -j ACCEPT
            iptables -D FORWARD -o $DEV -j ACCEPT
    	;;
        *)
    	exit 1
    esac
    exit 0
  • Create the configuration file /jffs/etc/openvpn/rtr2-to-rtr.ovpn on the client
    client
    remote rtr.yourdomain.com
    ns-cert-type server
    ca ca.crt
    cert rtr2.crt
    key rtr2.key
    tls-auth ta.key 1
    tls-client
    cipher bf-cbc
    auth sha1
    resolv-retry infinite
    nobind
    persist-key
    persist-tun
    mtu-disc yes
    fast-io
    verb 4
    mute 5
    management 127.0.0.1 5001
    management-log-cache 50
  • Start the OpenVPN client
    /jffs/etc/config/rtr2-to-rtr.wanup
  • See how things are going.  Note that you can also use the WebGUI (Status > OpenVPN) to inspect the OpenVPN log messages.
    more /tmp/rtr2-to-rtr/log

Bridging example: Remote Access

OpenVPN can also be used to connect remote computers to a local area network.  The VPN gives the remote computers access to resources on the local area network such as files, printers, databases or internal websites.

In this example, one DD-Wrt router is configured as an OpenVPN server.  Road warrior clients can connect to this router to gain access to the server’s LAN.  The systems are:

  • rtr2, the server with:
    • LAN network 10.0.2.0/24
    • WAN address rtr2.yourdomain.com
  • client, connecting from a public IP address, running either

rtr2-server

Certificates

  • On the Linux host, create certificates and corresponding secret keys for the server and clients.
    ./build-key-server rtr2-server  # common name = rtr2-server, no passwd
    ./build-key        username1    # common name = username1, no passwd
    ./build-key        username2    # common name = username2, no passwd

OpenVPN Server

  • From the Linux host, copy the certificates/keys files to /jffs/etc/openvpn on the server.
    ssh rtr mkdir -p /jffs/etc/config
    ssh rtr mkdir -p /jffs/etc/openvpn/ccd
    scp ca.crt dh2048.pem ta.key rtr-server.crt rtr-server.key rtr:/jffs/etc/openvpn/
  • Create a startup script /jffs/etc/openvpn/config/rtr2-server.wanup on the server. Make the script executable (chmod 755). DD-Wrt will call this script each time WAN link comes up.
    #!/bin/sh
    
    BR=br0
    DEV=tap0
    ETHDEV=eth0
    PORT=1194
    PROTO=udp
    
    BASE=$(basename $0)
    NAME=${BASE%\.*}
    TMP=/tmp/$NAME
    PID=$TMP/pid
    LOG=$TMP/log
    #LOG=/dev/null
    DIR=$(dirname $0)/../openvpn
    
    [ -d $TMP ] || mkdir -p $TMP
    echo "$(date) $NAME: WAN up" >$LOG
    
    if [ -e $PID ] ; then
        kill -0 `cat $PID`
        if [ $? -eq 1 ] ; then
    	rm $PID  # remove false PID
        fi
    fi
    
    if [ -e $PID ] ; then
        kill -HUP `cat $PID`  # restart
    else
        rm $PID
    
        ln -s /usr/sbin/openvpn $TMP/openvpn
        $TMP/openvpn \
    	--daemon --writepid $PID --log-append $LOG \
    	--cd $DIR --config $NAME.ovpn \
    	--dev $DEV --port $PORT --proto $PROTO \
    	--up "$DIR/$NAME.helper $BR $DEV $PORT $PROTO up" \
    	--down "$DIR/$NAME.helper $BR $DEV $PORT $PROTO down" \
    	--ifconfig-pool-persist $TMP/ip-pool 86400
    fi
  • Create an helper script /jffs/etc/openvpn/rtr2-server.helper on the server. Make the script executable (chmod 755).
    Note that the tap interface will get a random MAC address and the bridge (br0) picking up the lowest address.  This confuses Windows Network Discovery in Windows 7, triggering a “Set Network Location” dialog.  To prevent this, you can take the last random MAC address and reuse it for subsequent tap interfaces.  (More details here.)

    #!/bin/sh
    BR=$1
    DEV=$2
    PORT=$3
    PROTO=$4
    case "$5" in
        'up')
    	#ifconfig $DEV hw ether enter_your_last_mac_address
            ifconfig $DEV 0.0.0.0 promisc up 
            brctl addif $BR $DEV 
            iptables -I INPUT -p $PROTO --dport $PORT -j ACCEPT
    	iptables -I INPUT   -i $DEV -j ACCEPT
    	iptables -I FORWARD -i $DEV -j ACCEPT
    	iptables -I FORWARD -o $DEV -j ACCEPT
    	;;
        'down')
    	brctl delif $DEV
    	ifconfig $DEV down
    	iptables -D INPUT -i $DEV -j ACCEPT
    	iptables -D INPUT -p $PROTO --dport $PORT -j ACCEPT
    	iptables -D FORWARD -i $DEV -j ACCEPT
    	iptables -D FORWARD -o $DEV -j ACCEPT
    	;;
        *)
    	exit 1
    esac
    exit 0
  • Create the configuration file /jffs/etc/openvpn/rtr2-server.ovpn on the server. Note that port 5002 is used by the /etc/openvpn*.shscripts.
    server-bridge 10.0.2.1 255.255.255.0 10.0.2.150 10.0.2.159
    ca ca.crt
    cert rtr2-server.crt
    key rtr2-server.key
    dh dh2048.pem
    tls-auth ta.key 0
    keepalive 10 120
    cipher bf-cbc
    auth sha1
    mtu-disc yes
    topology subnet
    client-to-client
    push "route 10.0.1.0 255.255.255.0 10.0.2.1" # optional across other VPN
    fast-io
    verb 4
    mute 5
    management 127.0.0.1 5002
    management-log-cache 50
  • Start the OpenVPN server
    /jffs/etc/config/rtr2-server.wanup
  • See how it is going. Note that you can also use the WebGUI (Status > OpenVPN) to inspect the OpenVPN log messages.
    more /tmp/rtr2-server/log  # a treasure trove of debug information
    netstat -lu                # should be listening on port 1194
    iptables -nvL              # INPUT chain should have port 1194 open

OpenVPN Server (using WebGUI)

Alternatively, the server can be configured using the DD-Wrt WebGUI.  The config will be stored in nvram, except for the large certificates and key files that are stored on a memory stick.  In this configuration, DD-Wrt generate matching files in /tmp/openvpn/.

  • Copy the certificates/keys files.  Remember to limit access to the key files (chmod 600).
    • ca.crt
    • rtr2-server.crt
    • rtr2-server.key
    • dh2048.pem
    • ta.key
  • Services > VPN > OpenVPN Server.  These values will be used to create the configuration file /tmp/openvpn/openvpn.conf
    • Start OpenVPN Server = enable
    • Start Type = WAN up (after domain resolver is up)
    • Switch Server config = new Style
    • Pool IP = 10.0.2.150 .. 10.0.2.159
    • Gateway = 10.0.2.1
    • Netmask = 255.255.255.0
    • Port = 1194
    • Tunnel Protocol = UDP
    • Encryption Cipher = Blowfish CBC
    • Hash Algorithm = SHA1
    • Advanced Options = disable
    • The Cert, Key fields are left blank.  Instead use the Additional Config field to refer to the files on the USB stick  (see OpenVPN with key and certificate files via WebGUI).
      cert /jffs/rtr2.crt
      ca /jffs/ca.crt
      key /jffs/rtr2.key
      dh /jffs/dh2048.pem
      tls-auth /jffs/ta.key 0
      push "route 10.0.2.0 255.255.255.0"

OpenVPN Client, running Windows 10

  • Import Certificates (from paragraph 6.4.1 in my document SISO).
    • From the start menu run certmgr.msc
    • Under the Certificates, right-click on Trusted Root Certification Authorities. From All Tasks choose Import.
      • Click Next
      • Browse to select the Personal Information Exchange certificate CAcert.crt.
      • Click Next
      • Type in the password, and Mark this key as exportable
      • Click Next
      • Place all certificates in Personal Certificates Store
      • Click Next; Click Finish
      • Verify that the certificate is listed under the Root Certification Authorities
    • Under the Certificates, right-click on Personal. From All Tasks choose Import.
      • Click Next
      • Browse to select the Personal Information Exchange certificate (yourname.p12).
      • Click Next
      • Type in the password, and Mark this key as exportable
      • Click Next
      • Place all certificates in Personal Store
      • Click Next; Click Finish
      • Verify that the certificate displays correctly, and the Root CA is known.
  • Use explorer to show the directory “C:\Program Files (x86)\OpenVPN\config\”
    • Populate the directory with the certificate and key files from the Linux host:
      • ca.crt
      • username1.crt
      • username1.key
      • ta.key
    • Create the configuration file rtr2.ovpn in that same directory

      remote rtr2.yourdomain.com 1194
      client
      ca ca.crt 
      cert username1.crt 
      key username1.key 
      tls-auth ta.key 1
      ns-cert-type server
      dev tap
      proto udp 
      nobind 
      resolv-retry infinite 
      persist-key 
      persist-tun 
      remote-cert-tls server 
      float
      script-security 2
      cipher BF-CBC
      auth SHA1
      verb 3
      mute 5
  • With OpenVPN running, right-click on its icon in the task area (bottom right) > connect > rtr2.

OpenVPN Client, running OS X

  • Create folder rtr2.yourdomain.com on your Desktop.  (details can be found in the wiki)
    • Copy the certificate/key files to that folder
      • ca.crt
      • username2.crt
      • username2.key
      • ta.key
    • In that folder, create configuration file rtr2.ovpn
      remote rtr2.yourdomain.com 1194  # update me
      client
      ca ca.crt 
      cert username2.crt 
      key username2.key 
      tls-auth ta.key 1
      ns-cert-type server
      dev tap
      proto udp 
      nobind 
      resolv-retry infinite 
      persist-key 
      persist-tun 
      remote-cert-tls server 
      float
      script-security 2
      cipher BF-CBC
      auth SHA1
      verb 3
      mute 5
  • Append the extension .tblk to the folder name. This will change the folder icon into a Tunnelblick VPN Configuration.
  • Double-click the folder’s new icon to install it. Choose private configuration when asked.
Coert Vonk

Coert Vonk

Independent Firmware Engineer at Los Altos, CA
Welcome to the things that I couldn’t find.This blog shares some of the notes that I took while deep diving into various fields.Many such endeavors were triggered by curious inquiries from students. Even though the notes often cover a broader area, the key goal is to help the them adopt, flourish and inspire them to invent new technology.
Coert Vonk

Latest posts by Coert Vonk (see all)

1 comment to DD-WRT and OpenVPN (without nvram footprint)

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

  

  

  

Protected with IP Blacklist CloudIP Blacklist Cloud