OpenVPN: Listen on TCP and UDP with TUN

Share this post...

Today I’ll describe how to get OpenVPN to listen both to UDP and TCP port, using both tun device and the same network for clients. Meaning the same client can connect on either TCP or UDP and get the same IP Address assigned.

To achieve this, we’re gonna need:

  • OpenVPN
  • Sudo

I’m running this on a Debian Wheezy installation, but any Linux distribution can do the trick.

Let’s create the first OpenVPN instance, to listen on UDP/1194, creation of certificate is not covered by this HOWTO, as plenty of resources can already be found online.

Note that, the only difference with a standard VPN configuration, is the following line:


learn-address /etc/openvpn/MYVPN/learn-address.sh

This configuration line will make the script ‘learn-address.sh’ be ran whenever a client’s address is learned or unlearned. This will allow us to modify the kernel’s routing table upon client connection/disconnection and specify which tunnel interface we should use for that particular client.

Let’s now configure the TCP VPN:

Note that we only changed the name of log files, IPP, the tun device name and of course, the protocol.

Now, let’s configure a client’s CCD file like the following:

Allow vpn user to run /sbin/ip through sudo, to be able to make routing table changes within the learn-address.sh script; Let’s create a ‘/etc/sudoers.d/openvpn’ file with the following content:


vpn ALL=(ALL:ALL) NOPASSWD: /sbin/ip

And last but not least, let’s put together the learn-address.sh script which will make all the magic:

As some comments have mentioned, you also need to run these:


# adduser --system --group vpn
# chmod +x learn-address.sh

Now, you can try and make your client connect to the UDP instance, disconnect and connect again to the TCP one. You can tail -f the /tmp/learn.log file in which you can see routing changes if everything is working:


[-] Adding addr 10.1.2.6 -> tun0
[-] Deleting addr 10.1.2.6 ->
[-] Adding addr 10.1.2.6 -> tun1
[-] Deleting addr 10.1.2.6 ->

This was useful to you? Have questions? Thoughts? Don’t hesitate to leave a comment if so.

Share this post...

15 thoughts on “OpenVPN: Listen on TCP and UDP with TUN”

  1. I’ve tried this but it seems as if the route is not being added by the script located in : /etc/sudoers.d/openvpn
    I see my server does not have a vpn user and group and openvpn uses nobody:nogroup to operate.
    How can I get the script to run without adding “nobody” to the sudoers list?
    Will it help to manually add a vpn user and group and specify it in the openvpn config file?

    1. Sorry for the lack of timely reply.
      Yes indeed, you need to add an vpn user (I generally also put a vpn group) and specify them in the configuration…

      HTH,

      –Thomas

  2. I’ve noticed you have 255.255.254.0 as the subnet in the UDP configuration and 255.255.255.0 in the TCP configuration (3rd octet variance). Is this intended or a typo?

    Thanks for the great article,
    Derek

  3. Thanks for that script but I have the situation of one client connecting as UDP receive a assigned IP for tun0 and another client connecting as TCP receive the same IP but for tun1 this seems understandable since both openvpn server instances are not updating same pool of available IPs. I’m not sure if using same IPP file would help here?

  4. Ha, this is maybe because I don’t use
    client-config-dir /etc/openvpn/MYVPN/ccd
    ccd-exclusive

    I’m not sure about that purpose here: is it to bypass the pooling? so for each client a predefined IP.
    Therefore I believe I don’t well understand the usage of ifconfig-pool-persist ?
    mm I don’t wanted to assign specific IP for a client but just to keep that same IP range for two connection type…

    1. Yeah I kind of always wanted my client’s IPs to be public so I haven’t thought about anything else there.
      I don’t really know if pool-persist file could be shared amongst openvpn instance.. that’d be a thing to give a try!

      Let me know if you have more details! 🙂

      –Thomas

      1. Hi and thanks for this great tutorial! Finally an elegant solution without the need for using a bridge.
        As Michael Stilmant mentioned the options client-config-dir and ccd-exclusive aren’t really necessary. I went good without them. Instead I used the same “IP database” in both configs:

        ifconfig-pool-persist /etc/openvpn/…/ipp.txt 60
        (don’t know if the “60 seconds” are necessary)

        Two important commands you didn’t mention:
        # adduser –system –group vpn
        # chmod +x learn-address.sh

  5. Hello
    I run openvpn on centos 6.x and use radius IBSng for user and pass
    i have 3 user and a lot of people connect at the same time and im looking for a way people can connect udp or tcp what they want, can i use your article for that ?

  6. Hi,

    I know it is already some time since you wrote that article.
    I like it and followed it. And so far it is working well.

    Nevertheless I do have a question concerning the learn-address.sh script.
    You do use ${dev} there. A variable that is not given as a starting parameter by openvpn. It isn’t defined in the script either. And hence the command is failing.
    “ERROR: Linux route add command failed: external program exited with error status: 2”

    So what should this variable do and how does it get its value?

    I suppose you do have a working version of this. So could you please check? Thank you very much.

    Greetings, Alex

    1. Hi Alex, sorry for the late answer. I actually updated the script today to also add the “change” action OpenVPN has added, as I had some issue going on with clients not being routed properly because of this.
      On the ${dev} variable, this one is actually an environment variable that OpenVPN is setting up. If you don’t have it, maybe it’s because you don’t hardly specify it in your configuration, which might be the root cause, or not, maybe try adding it?

      Thanks for your interest!

      –Thomas

Leave a Reply

Your email address will not be published. Required fields are marked *