OpenWrt with OpenVPN client on TP-Link TL-MR3020 (rev 2)

Hey! Listen! There are a few posts about installing OpenWrt on these travel routers. Make sure you’re reading the latest version, below.

2016-04-28OpenWrt upgrade process
  • OpenWrt upgrade
  • 2015-08-26OpenWrt with OpenVPN server on TP-Link Archer C7
  • Initial post
  • 2015-02-15OpenWrt with OpenVPN client on TP-Link TL-MR3020 (rev 3)
  • Setup entirely through SSH instead of LuCI
  • Small tweaks
  • 2015-01-24OpenWrt with OpenVPN client on TP-Link TL-MR3020 (rev 2)
  • Added SAMBA share
  • Added alerting scripts
  • 2014-10-19OpenWrt with OpenVPN client on TP-Link TL-MR3020 (rev 1)
  • Replaced PPTP client with OpenVPN client
  • Replaced my home server with PIA server


    A few months ago, the team at OpenWrt released version 14.07 of OpenWrt, called Barrier Breaker. I’m going to be installing Barrier Breaker on my MR3020 and setting up an OpenVPN client. If you don’t know the difference between PPTP/IPSec/OpenVPN, IVPN has a great comparison chart.

    The “why”

    Before we get started, I need to get up on my soapbox and say a few things. If you’re not using a VPN, you should be. VPNs aren’t just for hackers, thieves, or people doing illegal things (you wouldn’t download a car, would you?). VPNs are great for:

    • Adding a layer of security to your browsing (VPNs encrypt everything!)
    • Securely connecting two routers to create one large network over the internet (businesses do this all the time)
    • Students/employees connecting back to their university/office to work remotely (again, a common practice)
    • Circumventing geoblocking (i.e., content blocked based on your physical location)
      • Watch Netflix from outside the US
      • Watch BBC iPlayer from outside the UK
    • FREEDOM OF SPEECH! Use a VPN to circumvent government restrictions put on the internet (e.g., China, Iran, North Korea, etc…)

    But, I digress. VPNs are great. If you don’t have one, get one. Personally, I like using Private Internet Access (I’m not compensated, just a happy customer). If you don’t know where to start, TorrentFreak has a great article on which VPN services take your anonymity seriously (newsflash, PIA is at the top of their list).

    The “how”

    My plan for this router is to use it when I travel. I plan on plugging it into the Ethernet port in a hotel (or my house/friend’s house/Airbnb host) and having it broadcast a wireless network. Since the router is an OpenVPN client, any devices that join that wireless network will be VPNed into PIA’s servers. Eventually, when I install OpenVPN on my home router, I can route my connection back home. It’s probably easier illustrated than explained, as below.20141018_002

    There are a few advantages to this setup, as opposed to installing the OpenVPN client on each device:

    • Devices that don’t support OpenVPN can be protected (XBox, Roku, etc…)
    • Multiple machines (phone, laptop, Roku, etc…) can share one VPN connection
    • You can switch between an insecure wireless network (home/friend’s house/hotel) and a secure wireless network (OpenWrt) whenver needed

    Ready? Let’s get started!

    Install OpenWrt

    I’m going to assume you’re already running Attitude Adjustment (AA) and want to upgrade to Barrier Breaker (BB).

    Disconnect your PC from all wired and wireless networks, then connect your MR3020 to your PC. Because of the way I have this router setup from my previous post (eth0 is a DHCP client, not server), I’m going to connect to the WiFi network it’s broadcasting. I checked my IP, opened my browser, and navigated to (your address will be as stock). I then logged in with the username and password I had set before.

    Once you’re logged into OpenWrt, go to the System tab, then the Backup/Flash Firmware tab. At this point, it’s a good idea to make a backup of your config by pressing Generate Archive.20140614_022

    As-of this writing, the OpenWrt wiki page for the MR3020 doesn’t list BB as the newest firmware. There are two options for the BB download: a file for the factory firmware, and a file for upgrading. We’ll choose the upgrade.

    Back at your router, under Flash new firmware image, make sure Keep settings is checked to keep your current settings. I’m going to uncheck this, since I want to start from scratch. Browse to your downloaded firmware, and press Flash image to upgrade it.20141018_004

    Verify the checksum, and press Proceed to continue.20141018_005

    Wait a few minutes and the router will reboot. Check your IP again after it’s back up, as mine had changed since I erased my settings.20141018_006

    At this point, my MR3020 is running stock BB and I’m going to reconfigure it from scratch. Since the wireless network was wiped out, I’m going to reconnect with Ethernet.

    Configure OpenWrt

    From here, the OpenWrt wiki page recommends going through the basic configuration for any OpenWrt installation. I’m going to be combining some of the basic configuration with my configuration for the VPN client.

    Navigate to in your browser and you’ll be greeted by LuCI, the web interface for OpenWrt. OpenWrt recently switched to the Unified Configuration Interface, also known as UCI. The UCI is basically a collection of easy-to-read configuration files that are all centrally located, making OpenWrt much easier to configure. What’s nice about LuCI is that it reads/writes from/to the UCI files. Any changes you make in LuCI are reflected in the UCI files, and vice versa, meaning you can configure the MR3020 from the web interface, or from the command line.

    Anyway, moving on. Leave the username as  “root” and the password field empty. Press Login to continue.20141018_007

    Set a password

    From the main status screen, we’re going to set a root password by using the link in the yellow box at the top of the page. If you haven’t noticed, LuCI is a lot easier on the eyes in BB than AA.20141018_008

    Here, you can (and should) set a root password, as well as setup SSH access (which we’ll need later). Press Save & Apply to continue.


    Look for Password successfully changed! at the top of the screen.20141018_010

    Verify SSH access by using PuTTY or another SSH client.

    [logan@Arch ~]$ ssh root@
    root@'s password: 
    BusyBox v1.22.1 (2014-09-20 22:01:35 CEST) built-in shell (ash)
    Enter 'help' for a list of built-in commands.
      _______                     ________        __
     |       |.-----.-----.-----.|  |  |  |.----.|  |_
     |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
     |_______||   __|_____|__|__||________||__|  |____|
              |__| W I R E L E S S   F R E E D O M
     BARRIER BREAKER (14.07, r42625)
      * 1/2 oz Galliano         Pour all ingredients into
      * 4 oz cold Coffee        an irish coffee mug filled
      * 1 1/2 oz Dark Rum       with crushed ice. Stir.
      * 2 tsp. Creme de Cacao

    Setup NTP

    The MR3020 doesn’t have a real-time clock or CMOS battery. Because of this, every time it loses power, the clock resets to October 1st. To circumvent this, we’re going to use NTP to get our time from the internet. You don’t have to setup NTP, but it makes troubleshooting easier when you’re looking at timestamped log files. Keep in mind, since the MR3020 is connected directly to your PC (not the internet), this won’t take effect until after we get it online. Don’t freak out if it’s not working right away.

    First, set a hostname, zone name, and time zone for your router. The list of zone names/time zones can be found here. Make note of the tick marks around the zonename.

    uci set system.@system[0].hostname=mr3020_home
    uci set system.@system[0].zonename='America/New York'
    uci set system.@system[0].timezone=EST5EDT,M3.2.0,M11.1.0
    uci commit system

    Next, we’re going to enable the NTP client. I’m using US servers from the NTP Pool Project, but change your servers as needed. Again, don’t forget the tick marks.

    uci set system.ntp=timeserver
    uci set system.ntp.enabled=1
    uci add_list system.ntp.server=''
    uci add_list system.ntp.server=''
    uci add_list system.ntp.server=''
    uci add_list system.ntp.server=''
    uci commit system

    Alternatively, you can set all of this from the System dropdown by selecting System.

    Set default IP

    Next, we’re going to change the default IP of the router from to (or whatever scheme you want). Most devices ship with as the default, and since we’re going to be double-NATed, we can’t have two identical IPs on the same network.

    Go to the Network dropdown, then select Interfaces. Select Edit on the LAN interface (which is actually a bridge of the wired and wireless interfaces). Under Common Configuration, then the General Setup tab, change the IPv4 address field from to (or whatever scheme you want).


    Under DHCP Server, then the General Setup tab, you can also limit the number of addresses available in the DHCP pool (optional). Press Save & Apply to continue.20141018_013

    You’ll have to reboot your MR3020, and then check your IP settings again to verify the change. Log back into the web interface at the new address using your new root password.

    Create wireless network

    We need to create a wireless network for the MR3020 to broadcast. Eventually, we’re going to turn off LAN access on the Ethernet port, and we’ll need a way to connect to the router locally.

    Go to the Network dropdown, then select Wifi. Select Enable on the wireless network. Once enabled, select Edit. Setup your network as needed (name, channel, etc…).20141018_014

    Under Interface Configuration, then the Wireless Security tab, you can choose a WiFi password, preferably WPA2-PSK. Remember, this device may be a direct link back to your home network. Even if you have a strong VPN password, a weak WiFi password with weak encryption (e.g., WEP) could compromise your network. Press Save & Apply to continue.


    At this point, you should disconnect the Ethernet cable from the MR3020 and connect to the WiFi network we just setup. Normally, it’s not recommended to configure routers over wireless, but since we’re not going to be transferring files or upgrading firmware, we should be ok.

    Setup WAN interface

    We need the MR3020 to request an IP address from another router when it is plugged in. For this, we’ll need to make a new interface that will act as a DHCP client.

    Go to the Network dropdown, then select Interfaces. Here, you can see the default interface, br-lan, which is a bridge of the wired and wireless interfaces. We’re going to create the WAN interface by pressing Add new interface at the bottom of the screen. Name the interface something like WAN, with the protocol being set to DHCP client, covering the eth0 interface. Press Submit to continue.


    On the next screen, under Common Configuration, go to the Firewall Settings tab and select WAN. Press Save & Apply to continue.20141018_017

    Unbridge LAN interfaces

    By default, the wired and wireless interfaces are bridged. I want them to be separate, so that I can plug the MR3020 into another router and use the wireless interface of the MR3020 to broadcast a SSID. Essentially, I making it so that only another router can use the Ethernet port, and only clients can use the wireless network. If you don’t unbridge the interfaces, you’ve basically just created a wireless AP for the other router.

    Go to the Network dropdown, then select Interfaces. Select Edit on the LAN interface. Under Common Configuration, then the Physical Settings tab, uncheck the box for Bridge interfaces. Then, check the radio button next to the OpenWrt (or whatever you named it) wireless network. Press Save & Apply to continue, then reboot your MR3020.20141018_018

    Verify internet access

    At this point, plug your MR3020 into a LAN port on your other router, and connect your PC to the MR3020’s wireless network. It doesn’t matter what IP your MR3020 gets from the other router, as your PC should see the MR3020 as (or whatever your made it).

    You should be able to access the internet, as well as ping websites through SSH. In addition, go to the Status dropdown, then select Overview to make sure your Local Time field is updated with the correct time, now that we’re on the internet.

    Congratulations, you are now double-NATed.

    Configure extroot

    Before we get started, we need to make some space for OpenVPN. The MR3020 only has 4MB of flash. After OpenWrt is installed, we’re only left with about 400KB, which won’t be enough for the 600KB+ of OpenSSL libraries we’ll need, in addition to other packages that will make life easier.

    root@OpenWrt:~# df -h
    Filesystem                Size      Used Available Use% Mounted on
    rootfs                  640.0K    228.0K    412.0K  36% /
    /dev/root                 2.3M      2.3M         0 100% /rom
    tmpfs                    14.1M    448.0K     13.7M   3% /tmp
    /dev/mtdblock3          640.0K    228.0K    412.0K  36% /overlay
    overlayfs:/overlay      640.0K    228.0K    412.0K  36% /
    tmpfs                   512.0K         0    512.0K   0% /dev

    There are two ways around this:

    1. ExtRoot, which can either extend or move the root filesystem to a USB flash drive
    2. Build a custom image of OpenWrt from scratch, leaving out unnecessary packages

    My first instinct was to build a custom image, leaving out PPP. However, after a few hours of trying and multiple images, it still didn’t give me the space I needed. The only way to get OpenVPN on the MR3020 would be to leave LuCI out of the image, and I’m not willing to give that up.

    That leaves me with using a USB flash drive to extend or move the filesystem. Thankfully, setting up ExtRoot is pretty easy, and we won’t need a huge flash drive since we’re only after a few extra MB. I picked up this flash drive since it’s small.

    Start by reading the theory on ExtRoot, then go over the how-to guide. You need to decide whether you’ll be using external overlay (also called pivot-overlay) or external root (also called pivot-root). Essentially, external overlay extends the root filesystem to include the flash drive, while external root copies the root filesystem to the flash drive, then boots from the flash drive. External root has a couple advantages (that I can see):

    • You can boot from multiple flash drives, each with a different config (one flash drive with a config for home, another with a config for traveling, etc…)
    • If the flash drive dies, OpenWrt still boots from the internal root filesystem (without all your configs)

    In this guide, I’m going to be using external root.

    Since my flash drive is 8GB, I’m going to give 1GB to OpenWrt and use the rest for a SAMBA share. Any device connected to my router will have access to the other ~6.5GB share. Get stared by formatting your flash drive with two ext4 partitions. I did this on my machine using GParted, but if you’re running Windows you can use this to format the drive. In this case, /dev/sda1 is 1GB and /dev/sda2 is the remaining ~6.5GB.

    Next, SSH into your router, and install a few packages. You’ll need to install the kmod-fs package for the type of filesystem that you’re using (e.g., kmod-fs-ext4, kmod-fs-ext3, kmod-fs-btrfs, etc…)

    opkg update
    opkg install block-mount kmod-usb-storage kmod-fs-ext4

    There’s a good chance your kernel modules didn’t load. If that’s the case, reboot your router and then plug in your flash drive.

    kmod: failed to insert /lib/modules/3.10.49/sd_mod.ko
    Configuring kmod-usb-storage.
    Configuring kmod-crypto-hash.
    Configuring kmod-lib-crc16.
    Configuring block-mount.
    Configuring kmod-fs-ext4.
    kmod: failed to insert /lib/modules/3.10.49/ext4.ko

    Find the name of your flash drive with the block info command. Mine was /dev/sda. Notice there are two partitions on the drive.

    root@mr3020_home:~# block info
    /dev/mtdblock2: UUID="20ad40ea-d33a421e-785b7d2d-ada99230" VERSION="4.0" TYPE="squashfs"
    /dev/mtdblock3: TYPE="jffs2"
    /dev/sda1: UUID="bf19ec66-8d6a-40d7-b366-f99f48cced33" NAME="EXT_JOURNAL" VERSION="1.0" TYPE="ext4"
    /dev/sda2: UUID="5ca298db-53d6-406c-9db3-344c2e5ebae8" NAME="EXT_JOURNAL" VERSION="1.0" TYPE="ext4"

    Create two directories and mount /dev/sda1 and /dev/sda2 on them.

    mkdir /mnt/batman
    mount /dev/sda1 /mnt/batman
    mkdir /mnt/network
    chmod -R 777 /mnt/network
    mount /dev/sda2 /mnt/network

    Now, copy the router’s internal flash to the flash drive. Obviously, replace /mnt/batman with whatever mount point you’re using.

    mkdir -p /tmp/cproot
    mount --bind / /tmp/cproot
    tar -C /tmp/cproot -cvf - . | tar -C /mnt/batman -xf -
    umount /tmp/cproot

    Your flash drive now has a copy of the router’s root filesystem on it (so don’t lose it). However, the router will still boot from its internal memory, so we need to change that by editing the /etc/config/fstab file. In addition to that, we’re also creating an extra entry for our SAMBA fileshare.

    cat >> /etc/config/fstab << EOF
    config mount
            option target        /
            option device        /dev/sda1
            option fstype        ext4
            option options       rw,sync
            option enabled       1
            option enabled_fsck  0
    config mount
            option target        /mnt/network
            option device        /dev/sda2
            option fstype        ext4
            option options       rw,sync
            option enabled       1
            option enabled_fsck  1

    Reboot your router. When it starts up, check your mount points and you should see that /dev/sda1 has been mounted on /, while /dev/sda2 has been mounted on /mnt/network.

    oot@mr3020_home:~# mount
    rootfs on / type rootfs (rw)
    /dev/root on /rom type squashfs (ro,noatime)
    proc on /proc type proc (rw,noatime)
    sysfs on /sys type sysfs (rw,noatime)
    tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noatime)
    /dev/sda1 on / type ext4 (rw,relatime,data=ordered)
    tmpfs on /dev type tmpfs (rw,relatime,size=512k,mode=755)
    devpts on /dev/pts type devpts (rw,relatime,mode=600)
    /dev/sda2 on /mnt/network type ext4 (rw,sync,relatime,data=ordered)
    debugfs on /sys/kernel/debug type debugfs (rw,noatime)

    If you check your space again, you’ll see that your root filesystem is now larger, and you have a second partition for SAMBA.

    root@mr3020_home:~# df -h
    Filesystem                Size      Used Available Use% Mounted on
    rootfs                  975.9M     10.5M    898.2M   1% /
    /dev/root                 2.3M      2.3M         0 100% /rom
    tmpfs                    14.1M    360.0K     13.7M   2% /tmp
    /dev/sda1               975.9M     10.5M    898.2M   1% /
    tmpfs                   512.0K         0    512.0K   0% /dev
    /dev/sda2                 6.2G     14.5M      5.9G   0% /mnt/network


    Setup VPN

    Now we need to install the OpenVPN client and configure it. The VPN termination point is going to be one of PIA’s servers, but it could be any OpenVPN server. You should read OpenWrt’s VPN overview, as well the OpenVPN beginner’s guide and the client guide.

    First, we’ll need to install a couple packages: openvpn-openssl for obvious reasons, the real version of wget to downlad the configuration files from PIA, and unzip to unzip the downloaded files. This is easiest done by connecting through SSH and running the commands below.

    opkg update
    opkg install openvpn-openssl wget unzip
    mv /etc/config/openvpn /etc/config/openvpn_old

    Unfortunately, there is no LuCI package for OpenVPN in BB, like there is in AA. As-of this writing, the package luci-app-openvpn is marked as broken. There appears to be a package for Chaos Calmer, located here, but I don’t think that will work in BB. That means we’re doing everything from the command line, which isn’t as intimidating as it may sound. Plus, since LuCI runs from the UCI files, we’ll be able to see some of our changes in LuCI when we log back in.

    We’ll need to create a new interface for the VPN by using the commands below. Name the network interface whatever you’d like, and name the physical (even though it’s virtual) interface tun0.

    cat >> /etc/config/network << EOF
    config interface 'PIA_VPN'
        option proto 'none'
        option ifname 'tun0'

    Download the OpenVPN configuration files from PIA.

    cd /etc/openvpn
    wget --no-check-certificate

    Now, we need to edit each .ovpn file in /etc/openvpn to include your username and password. However, it’s a pain editing every file manually. Instead, we’ll create a single file that stores your login credentials. The file I created is called authuser. Substitute your username and password, obviously.

    cat >> /etc/openvpn/authuser << EOF

    However, now we’ll have to go back and edit each .ovpn file to look for the authuser file, which is still too much work for me. If you look at each .ovpn file, you’ll see the only difference between them is the server address. What if we created a generic .ovpn connection file which omitted the server address (and port), but specified to use the authuser file? We could pass the server address and port as an option in our command to start the VPN connection.

    Create the file with the command below. See how we removed the line for the server/port, and added a line for the authuser file and auth-nocache options?

    cat >> /etc/openvpn/piageneric.ovpn << EOF
    dev tun
    proto udp
    resolv-retry infinite
    ca ca.crt
    remote-cert-tls server
    auth-user-pass authuser
    verb 1
    reneg-sec 0
    crl-verify crl.pem
    keepalive 10 120

    To compare, this is the US East.ovpn file…

    dev tun
    proto udp
    remote 1194
    resolv-retry infinite
    ca ca.crt
    remote-cert-tls server
    verb 1
    reneg-sec 0
    crl-verify crl.pem

    Now, we need to create a new firewall zone for this VPN connection. This is actually the same config as the WAN zone, but it’s easier to make a new zone in case we need to change anything in the future. Name the firewall zone, and substitute the network interface name you created above. We’ll also be forwarding LAN traffic to the VPN.

    cat >> /etc/config/firewall << EOF
    config zone
        option name 'VPN_FW'
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option network 'PIA_VPN'
    config forwarding                               
            option dest 'VPN_FW'                    
            option src 'lan' 

    Reboot your router. If you’d like, you can login to LuCI and see the new network interface, physical interface tun0, and firewall zone. Back on the command line, use the following command to start the VPN. Specify your generic configuration file and choose a server from PIA, as well as a port number.

    openvpn --cd /etc/openvpn --config /etc/openvpn/piageneric.ovpn --remote 1194

    If everything went well, you should see something like below, ending in Initialization Sequence Completed. If not, you did something wrong. Check your logs and start looking here.

    root@mr3020_home:~# openvpn --cd /etc/openvpn --config /etc/openvpn/piag
    eneric.ovpn --remote 1194
    Sat Jan 24 13:16:20 2015 OpenVPN 2.3.6 mips-openwrt-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [MH] [IPv6] built on Jan  6 2015
    Sat Jan 24 13:16:20 2015 library versions: OpenSSL 1.0.1k 8 Jan 2015, LZO 2.08
    Sat Jan 24 13:16:20 2015 WARNING: file 'authuser' is group or others accessible
    Sat Jan 24 13:16:20 2015 UDPv4 link local: [undef]
    Sat Jan 24 13:16:20 2015 UDPv4 link remote: [AF_INET]
    Sat Jan 24 13:16:21 2015 [Private Internet Access] Peer Connection Initiated with [AF_INET]
    Sat Jan 24 13:16:24 2015 TUN/TAP device tun0 opened
    Sat Jan 24 13:16:24 2015 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
    Sat Jan 24 13:16:24 2015 /sbin/ifconfig tun0 pointopoint mtu 1500
    Sat Jan 24 13:16:24 2015 Initialization Sequence Completed

    Check ifconfig to see if you have a tunnel interface. If you do, that’s good!

    tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
              inet addr:  P-t-P:  Mask:
              RX packets:78 errors:0 dropped:0 overruns:0 frame:0
              TX packets:102 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:100 
              RX bytes:10371 (10.1 KiB)  TX bytes:83342 (81.3 KiB)

    Next, try to get on the internet. If you can’t, there’s a good chance it’s a DNS issue. To resolve this, we’ll set our DNS servers on this connection to use PIA’s DNS servers. It looks like we can set the DNS servers for a specific interface, so we’ll add them for the LAN interface.

    uci add_list dhcp.lan.dhcp_option="6,,"
    uci commit dhcp

    Now we should have DNS working, and since we’re using PIA VPN and PIA DNS servers, we shouldn’t have any DNS leaks either. After all, what’s the point of a VPN if your DNS leaks?

    Restart your VPN connection and try it again! Check your IP with an external tool, like WhatIsMyIP, both on your local wireless network, as well as the OpenWrt network. You should see the difference, meaning you are successfully connected!

    Before (on my local network)…20141018_020After (VPNed in)…20141018_021

    Now is a good time to check for DNS leaks while on the VPN. Basically, we’re looking to make sure the server giving us our IP address is also providing DNS. If it’s not, then your ISP probably providing DNS, and they’re able to see every DNS request you make.


    Note – A commenter found that his DNS was still leaking when set on the LAN interface. He suggested setting it on the WAN interface, as seen here.

    Run at startup

    If you’d like your VPN connection to run at startup, edit the /etc/rc.local file. Paste your connection string before exit 0. The ampersand tells OpenWrt not to output anything to the screen.


    # Put your custom commands here that should be executed once
    # the system init finished. By default this file does nothing.
    exit 0


    # Put your custom commands here that should be executed once
    # the system init finished. By default this file does nothing.
    openvpn --cd /etc/openvpn --config /etc/openvpn/piageneric.ovpn --remote 1194 &
    exit 0

    Alternatively, you can also set this from the System dropdown by selecting Startup.

    Setup SAMBA

    Next, we need to setup the SAMBA share by installing the SAMBA packages. You’ll need to search opkg for the correct version of SAMBA server.

    opkg update
    opkg list | grep -i samba
    opkg install luci-app-samba samba36-server
    /etc/init.d/samba enable
    /etc/init.d/samba start

    Following the last step of this guide (thanks to commenter Ben for sending this in!), go to the Services dropdown, then select Network Shares. Under Samba, select a hostname.


    Then, under Shared Directories, select Add. Here, specify a share name and path (in our case, /mnt/network), and select Allow guests. If you don’t want guest access, you’ll need to setup users as described here. Press Save & Apply to continue, then reboot your MR3020.


    Connect to your router’s wireless network, then, in your file manager (assuming you’re using Linux, since this is a ext4 SAMBA share), navigate to your share name (e.g., smb://openwrt/box/).


    Note – Your distro may not have a SAMBA client installed. On Arch Linux, I needed the smbclient and gvfs-smb packages.

    Setup alerting scripts

    The whole point of this build was to create a connection that is always on and safe for you to use. But, what if the VPN tunnel goes down? You’d still  be able to pass traffic, but it would be over the regular internet, not the encrypted VPN connection. There are a couple ways around this, including putting a route in that denies traffic when not on the VPN, as well as setting up alerting scripts to let you know when the tunnel goes down. Here, I’ll be doing the latter.

    On the The OpenVPN 2.3 man page, there are options called –up and –down, which as they sound, run scripts when the VPN tunnel comes up or goes down. Basically, we’re going to create a script to email us when the tunnel comes up, and another script that will run when the tunnel goes down, but we still have internet access. Obviously, if the tunnel goes down as a result of the router being powered off or the WAN connection dying, the email isn’t going to work. We’re also going to implement a third script that will run out of root’s crontab to check for VPN connection status every 10 minutes.

    Setup email

    First, we need to install msmtp.

    opkg update
    opkg install msmtp

    If it’s not set already, you need to set the msmtp config file to be R/W for your account only. It won’t function otherwise.

    chmod 600 /etc/msmtprc
    cp -p /etc/msmtprc /etc/msmtprc_old

    We need an external STMP server that we can use to route messages through. Luckily, Google provides one for free if you have a Gmail account. Obviously, I would advise against using your primary Gmail account for this. Setting up a dedicated Gmail account just for this application only takes a few minutes and is worth it, in my opinion. Google’s SMTP settings are here, we’ll need them later.

    Next, we’re going to edit the config file at /etc/msmtprc to include our information. There is some pretty good documentation for msmtp over at SourceForge, so I won’t bother going over all the options below. Substitute your new Gmail username and password below.


    # Example for a system wide configuration file
    # A system wide configuration file is optional.
    # If it exists, it usually defines a default account.
    # This allows msmtp to be used like /usr/sbin/sendmail.
    account default
    # The SMTP smarthost.
    host mailhub.oursite.example
    # Construct envelope-from addresses of the form "user@oursite.example".
    #auto_from on
    #maildomain oursite.example
    # Use TLS.
    #tls on
    #tls_trust_file /etc/ssl/certs/ca-certificates.crt
    # Syslog logging with facility LOG_MAIL instead of the default LOG_USER.
    syslog LOG_MAIL


    account        default
    tls            on
    tls_certcheck  off
    logfile        /etc/openvpn/msmtp.log
    auto_from      on
    auth           on
    protocol       smtp
    port           587
    password       your_gmail_password
    syslog         LOG_MAIL

    Next, create three scripts in your /etc/openvpn folder and set permissions on them.

    touch /etc/openvpn/
    touch /etc/openvpn/
    touch /etc/openvpn/
    chmod 700 /etc/openvpn/
    chmod 700 /etc/openvpn/
    chmod 700 /etc/openvpn/

    The only difference between the script and script are the words UP and DOWN in the STATUS variable in each script. Replace your recipients as needed.

    #Logan Marchione
    HOSTNAME=`uci get system.@system[0].hostname`
    DAY=`date "+%A"`
    DATE=`date "+%Y-%m-%d"`
    TIME=`date "+%H:%M:%S"`
    EMAIL='Subject: VPN '$STATUS' on '$HOSTNAME'
    From: OpenWrt Monitor
    As of '$DAY', '$DATE' at '$TIME' the VPN tunnel on '$HOSTNAME' is '$STATUS'.'
    echo "$EMAIL" | msmtp -t $RECIPIENT

    The only difference between the script and script are the words UP and DOWN in the STATUS variable in each script. Replace your recipients as needed.

    #Logan Marchione
    HOSTNAME=`uci get system.@system[0].hostname`
    DAY=`date "+%A"`
    DATE=`date "+%Y-%m-%d"`
    TIME=`date "+%H:%M:%S"`
    EMAIL='Subject: VPN '$STATUS' on '$HOSTNAME' 
    From: OpenWrt Monitor
    As of '$DAY', '$DATE' at '$TIME' the VPN tunnel on '$HOSTNAME' is '$STATUS'.'
    echo "$EMAIL" | msmtp -t $RECIPIENT

    Now, edit the /etc/openvpn/piageneric.ovpn file. Here, we’ve added the up and down options and specified the appropriate scripts. We’ve also added the up-restart option, which allows scripts to be called for restarts as well as initial program start. Finally, I’ve set script-security level to 2, which allows calling of built-in executables and user-defined scripts.

    dev tun
    proto udp
    resolv-retry infinite
    ca ca.crt
    remote-cert-tls server
    auth-user-pass authuser
    verb 1
    script-security 2 
    reneg-sec 0
    crl-verify crl.pem
    keepalive 10 120

    This script calls ifconfig and looks for an adapter called tun0. If your adapter name is different, replace it as needed. Replace the line that starts the VPN manually (the same command we used earlier) with your connection string.

    #Logan Marchione
    RESULT=`ifconfig | awk '/^tun0/ {print $1;}'`
    DATETIME=`date "+%Y-%m-%d %H:%M:%S"`
    if [ "$RESULT" == tun0 ]
    	echo "$DATETIME - No restart needed" >> vpncheck.log&
    	echo "$DATETIME - Restart needed, see below" >> vpncheck.log&
    	openvpn --cd /etc/openvpn --config piageneric.ovpn --remote 1194 >> vpncheck.log&

    The script will need to run on a schedule, and for that, we’ll be using cron. First, we need to make a file for root’s crontab and then start crontab.

    touch /etc/crontabs/root
    /etc/init.d/cron start
    /etc/init.d/cron enable

    If you check logs…


    …you should see that cron was started.

    Sat Jan 24 14:17:04 2015 crond[1526]: crond: crond (busybox 1.22.1) started, log level 5

    Then, add your entry to crontab. Use the schedule below to setup your job.

    #Crontab Schedule
    # +---------------- minute (0 - 59) *=all
    # |  +------------- hour (0 - 23) *=all
    # |  |  +---------- day of month (1 - 31) *=all
    # |  |  |  +------- month (1 - 12) *=all
    # |  |  |  |  +---- day of week (0 - 6) (Sunday=0) *=all
    # |  |  |  |  |
    # *  *  *  *  * command to be executed
    # -- -- -- -- - ---------------------------------
    # 00 12 *  *  * some_command        # will run some_command at 12:00 (noon) daily
    #Run VPN check to check for tunnel up/down
    10 * * * * /etc/openvpn/ > /etc/openvpn/vpncheck.log

    Finally, restart cron for the changes to take effect.

    /etc/init.d/cron restart

    Once everything is updated, reboot your MR3020.

    Test alerting scripts

    Assuming your VPN is already up, use ps | grep openvpn to find and kill the OpenVPN process ID.

    root@mr3020_home:~# ps | grep openvpn
      911 root      3436 S    openvpn --cd /etc/openvpn --config /etc/openvpn/piageneric.ovpn --remote 1194
    root@mr3020_home:~# kill 911
    root@mr3020_home:~# ps | grep openvpn
     1380 root      1356 S    grep openvpn

    After you kill it, grep again and you should see that OpenVPN isn’t running. In a minute or so, you should you get an email alerting you that the VPN tunnel is down.20150124_001

    Now, start the VPN manually (using the same command we used earlier to test).

    openvpn --cd /etc/openvpn --config /etc/openvpn/piageneric.ovpn --remote 1194 &

    Here, you’ll see the output from OpenVPN starting, and you should see the script running.

    root@mr3020_home:~# openvpn --cd /etc/openvpn --config /etc/openvpn/piag
    eneric.ovpn --remote 1194 &
    root@mr3020_home:~# Sat Jan 24 13:56:03 2015 OpenVPN 2.3.6 mips-openwrt-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [MH] [IPv6] built on Jan  6 2015
    Sat Jan 24 13:56:03 2015 library versions: OpenSSL 1.0.1k 8 Jan 2015, LZO 2.08
    Sat Jan 24 13:56:03 2015 WARNING: file 'authuser' is group or others accessible
    Sat Jan 24 13:56:03 2015 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
    Sat Jan 24 13:56:03 2015 UDPv4 link local: [undef]
    Sat Jan 24 13:56:03 2015 UDPv4 link remote: [AF_INET]
    Sat Jan 24 13:56:05 2015 [Private Internet Access] Peer Connection Initiated with [AF_INET]
    Sat Jan 24 13:56:08 2015 TUN/TAP device tun0 opened
    Sat Jan 24 13:56:08 2015 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
    Sat Jan 24 13:56:08 2015 /sbin/ifconfig tun0 pointopoint mtu 1500
    Sat Jan 24 13:56:08 2015 tun0 1500 1542 init
    Sat Jan 24 13:56:12 2015 Initialization Sequence Completed

    Again, in a minute or so, you should you get an email alerting you that the VPN tunnel is up.20150124_002

    This is easy to test. Kill your OpenVPN process and wait until the script runs out of crontab. Then, you should see your tunnel come back up and get an email about it.


    Backup your config

    You did all this work, don’t lose it. Go to the System dropdown, then select Backup/Flash Firmware and press Generate Archive to download a backup of all your configuration files. I don’t believe this backs up custom scripts/files, so keep that in mind.


    Failsafe mode

    At some point during this build, you’ll probably break something and lock yourself out of your router. Thankfully, you’re not left with a paper-weight. OpenWrt includes a failsafe mode that will let you telnet to your router. Steps for the MR3020 are below.

    1. Power off your MR3020 and connect it to your PC via Ethernet
    2. Set your PC’s IP to with a subnet of and a gatway of
    3. Plug in the power to the MR3020
    4. When the WPS button starts to flash, slide the switch labeled 3G/4G/WISP/AP back and forth really fast. At this point, the WPS button will start blinking faster than it was before. You are in failsafe mode.
    5. Connect to the router via telnet and you should see that you are in failsafe mode.
      logan@fedora20 ~$ telnet
      Connected to
      Escape character is '^]'.
       === IMPORTANT ============================
        Use 'passwd' to set your login password
        this will disable telnet and enable SSH
      BusyBox v1.22.1 (2014-09-20 22:01:35 CEST) built-in shell (ash)
      Enter 'help' for a list of built-in commands.
        _______                     ________        __
       |       |.-----.-----.-----.|  |  |  |.----.|  |_
       |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
       |_______||   __|_____|__|__||________||__|  |____|
                |__| W I R E L E S S   F R E E D O M
       BARRIER BREAKER (14.07, r42625)
        * 1/2 oz Galliano         Pour all ingredients into
        * 4 oz cold Coffee        an irish coffee mug filled
        * 1 1/2 oz Dark Rum       with crushed ice. Stir.
        * 2 tsp. Creme de Cacao
    6. Now, you should be able to change your password by entering passwd, but you may get an error about the filesystem being read-only, like below.
      passwd: /etc/passwd: Read-only file system
      passwd: can't update password file /etc/passwd
    7. If that happens, enter mount_root, then try the passwd command again. At this point, you should be able to SSH into the MR3020.
    8. If you installed too many packages and filled up the filesystem, you can wipe it and do a factory reset with the command below.
    9. Reboot the router, as below.
      reboot -f
    10. Try to SSH in. If it doesn’t work (mine didn’t), telnet in again, set your password again, and then try SSH again.
    11. If you want to transfer a new firmware file to the router, you can do that with the SCP utility on your PC.
      scp /path/to/file.bin root@
    12. Then, flash the new firmware file with the command below.
      sysupgrade -v /path/to/file.bin

    HTTPS Support

    Out of the box, LuCI does not support HTTPS. For that, you’ll need a couple packages.

    opkg update
    opkg install luci-ssl uhttpd-mod-tls

    First, backup the original /etc/config/uhttpd file. Then, edit the file to change your certificate settings as needed.

    cp -p /etc/config/uhttpd /etc/config/uhttpd_old
    vi /etc/config/uhttpd


    config cert 'px5g'
            option days '730'
            option bits '1024'
            option country 'DE'
            option state 'Berlin'
            option location 'Berlin'
            option commonname 'OpenWrt'


    config cert 'px5g'
            option days '730'
            option bits '2048'
            option country 'US'
            option state 'Pennsylvania'
            option location 'Pennsylvania'
            option commonname ''

    Once you have your certificate setup, restart the webserver.

    /etc/init.d/uhttpd restart

    You should see a message similar to the one below.

    Generating RSA private key, 2048 bit long modulus
    Generating selfsigned certificate with subject 'C=US;ST=Pennsylvania;L=Pennsylvania;CN=;' and validity 20141021000526-20161020000526

    If you navigate to /etc, you should see a certificate and a key file.

    -rw-r--r--    1 root     root           828 Oct 20 20:50 /etc/uhttpd.crt
    -rw-r--r--    1 root     root          1192 Oct 20 20:50 /etc/uhttpd.key

    Open your browser, and instead of going to, go to If you get a scary looking error message, that’s ok. Click Advanced.


    Then, proceed to your site.


    In the URL bar, you’ll still probably see some intimidating red error message.20141018_026

    Upon further inspection, we can see this error is because the certificate wasn’t generated by a trusted CA (Thawte, Symatec, etc…).


    If you inspect the actual certificate, you can see it’s the certificate we created, so you know it can be trusted.


    That’s it! I’ll be tweaking this guide as I go, but let me know if anything is incorrect or missing. Also, a couple useful posts and sources I used when stuck are located here, here, and here.


    26 thoughts on “OpenWrt with OpenVPN client on TP-Link TL-MR3020 (rev 2)”

    1. Hi Logan, I think it is better to set the custom dns on the wan interface rather than the lan interface to avoid dns leaks. To activate these settings go to the advance tab and deselect “use dns servers advertised by peer”. Once this is deselected, the “use custom dns servers” option will be revealed.

      • I have mine set on the LAN interface but I’m not having any leaks. What is the difference of setting on the WAN interface vs the LAN interface?

        • For me, if I set it on the LAN interface I get a leak, whereas if I set it on the WAN interface I don’t. Just posting my observation in case it helps someone.

    2. This is a really helpful tutorial, Logan. Thanks!

      The problem with setting DNS on the interfaces is that those settings apply whether or not the vpn connection is up. An alternative is to set the DNS server in your vpnup script, using the address supplied by PIA. For example, add these lines to vpnup:
      mv /tmp/ /tmp/
      echo $foreign_option_1 | sed -e ‘s/dhcp-option DOMAIN/domain/g’ -e ‘s/dhcp-option DNS/nameserver/g’ > /tmp/
      echo $foreign_option_2 | sed -e ‘s/dhcp-option DOMAIN/domain/g’ -e ‘s/dhcp-option DNS/nameserver/g’ >> /tmp/
      echo $foreign_option_3 | sed -e ‘s/dhcp-option DOMAIN/domain/g’ -e ‘s/dhcp-option DNS/nameserver/g’ >> /tmp/

      This takes the DNS options pushed by PIA and updates your resolv.conf accordingly. You can add a line to vpndown like ‘mv /tmp/ /tmp/’ to restore your settings to a pre-vpn state, but I found that killing openvpn causes them to be reset anyways (at least on openwrt BB).

      This approach has two advantages: 1) you’re using PIA’s current DNS settings; 2) the settings are only in force while the VPN is up.

      • Thanks Ron! I may add this to a future version of this post. You say that those settings apply whether or not the VPN connection is up. Do you see any disadvantage in using PIA’s DNS servers all the time, instead of just when the tunnel is up?

        • Sorry I missed this reply earlier!

          In the case of PIA specifically, using their DNS servers all the time is probably not an issue. However, for other vpn situations, the DNS server might only be accessible when the VPN is up. For example, I run an openvpn server on my router at home, and use the MR3020 as a client when traveling. When connected on my home vpn, I use my home router’s internal DNS so I can resolve hosts on my home net. When the VPN is down however, I can’t reach it.

    3. I haven’t run the install yet, but just out of curiosity, will the wireless modem still run with this config or does the external drive always need to be in place? Another option may be to have duplicate code on an SD card in the modem if that code always needs to be there?

      • The external USB drive needs to be in place. If the external drive dies, OpenWrt still boots from the internal root filesystem (without all your configs)

        • Thanks Logan, so my other option, if I’ve read your threads correctly, is to revert to the PPTP config where everything is resident on the internal memory. I’m primarily interested in using this to access US TV when overseas without tying up my laptop. And yes, I’m getting ready to tinker and experiment, but I tend to get sucked in to these projects! Thanks!

            • Finally got OpenWRT running after tearing my hair out and figuring out my computer was hung and wouldn’t refresh the wireless network list! In any case, what I was saying was, I would like to build the logic to recognize my 3g/4g usb stick plus run a VPN. I’m concerned there is not enough internal memory on the 3020 for the stick plus OpenVPN but maybe for the more compact PPTP. I’m honestly doubtful, though, that there is enough room at all for any VPN plus the stick without external memory. My next solution would be to configure the SD card that rides in the USB stick as the external drive as well as have duplicate VPN code on a flash drive for when I’m not using the 3g/4g stick.

              • Will, I’m still confused. What are you using the USB stick for? Is it just a USB flash drive for storage space, or is it a 3G/4G radio that you’re going to use for network connectivity? If it’s just for storage, I don’t think you’ll need SAMBA unless you’re going to share it out to users. As long as it’s just for storage local to the router, you should be good.

    4. This was a great tutorial Logan, thanks. I reckon it saved me days of messing about…
      If you ever get around to expanding this, I would really like to know how to utilize an external wireless WAN in this setup, so that in a hotel room that has no ethernet, I could join their wireless and share that connection. I’ve tinkered myself, but the system alway becomes unavailable, and I start again!

      Thanks Again


      • Thanks! Always glad to help!

        I’ve all-but given up on using it at a hotel, since most use captive portals now. There is some discussion on an older post about using a wireless network on the WAN, it might be of some help.

          • I use a Netgear PR2000 Trek N300 device to share ‘pay-per-device’ wifi as found in some nasty hotels etc. I tried to get things working with my MR3020 but could never get it to work stably. The Netgear seems to work flawlessly and has a hardware switch to choose between wired/wireless WAN sharing. The only problem is the firmware is closed source, but it seems to work for the basic functions that are available. (Just plug your MR3020 into a LAN port on the PR2000 and you still get the VPN privacy benefits…)

            • Forgot to say, it works fine with captive portals too. Just connect the PR2000 to the (usually open security) hotel wifi, then connect your client device to the PR2000 (using wifi or one of its LAN ports). When you browse somewhere and get redirected by the captive portal, enter your voucher/code/whatever and the hotel network will register the PR2000’s mac address (not your client), so you can then proceed to connect other devices via the PR2000. 😉

    5. I followed your guide, but afterwards only few websites can be opened. Apparently only http sites, and not https. I would be grateful if you could shed some light. thanks.

      • Hmmm, that’s strange. I don’t have my MR3020 anymore to test, but it sounds like a possible DNS issue? Any kind of traffic (on any port) should be able to pass over the VPN…

    6. I got to the part when you changed the IP for the device to, I changed it to one of my own. I clicked Save and Apply as instructed, but the device wouldn’t save. It kept saying it was applying changes. Like an idiot, I panicked, then did a reboot of the device in the GUI; I can’t log into the device with either the new ip,, as the address or the stock from the root. Any help?!?!

    7. I wanted my device to be a dedicated always on VPN box. I used it to plug in devices that I want never to work without a VPN, for example my phone ATA using SIP. The alerts idea is nice, but I wanted it just to stop.

      Turns out this is really easy, just do the setup you have described, then remove the forwarding from the lan to the wan interface. You can do this be removing the forward from the /etc/config/firewall, or using Luci just untick the forwarding box in the firewall page.

      I nice project for someone to tackle would be to use the device indicator light to show the vpn status, maybe solid on if the VPN is running and flashing on/off as a alert if it is not.

      Then test it by turning the VPN on and off from the command line with a computer plugged in. You will have access when the VPN is running, and no access if it isn’t. This makes a nice headless solution.

      BTW, thanks for the nice write up, I am familiar with this stuff but it is great to have it collected in one place. The unbridging for the single connector on the MR3020 was a time saver!

      • Sounds like an interesting idea! There have been a few people that have posted about the lights, check the comments to see.


    Leave a Comment

    This site uses Akismet to reduce spam. Learn how your comment data is processed.