Resolving brief L2TP VPN sessions and disconnect errors

I recently abandoned macOS as a server solution (future blog post about that BS) and have switched to Ubuntu. The straw that broke the camels back for me was a reliable VPN solution that didn’t involve OpenVPN.

Don’t get me wrong, OpenVPN is fine, but the lack of built-in support for macOS, iOS, and iPadOS clients and having to rely on clunky or expensive client packages for support felt like a non-starter for me.

Instead, I wanted a simple L2TP or IKEv2 VPN. Virtually every OS has built-in client support for them and they’re plenty secure. Plus, it’s very easy to deploy to Apple devices using a .mobileConfig file.

With my rapid transition to Ubuntu, there’s been a steep learning curve on configuring services manually. This really bit me when it came to the idea of a “local IP” in the realm of VPNs.

The Great Mistake

I had used a fantastic script that automates the L2TP+IPsec VPN written by Lin Song. In fact, after I modified the script to use my home network’s subnet and ran it, my VPN was up and running in less than 5 minutes.

Literal years of trying to get an L2TP vpn working, all it took was an Ubuntu VM and one simple script.

Well, sort of.

After a range of three to ten minutes, the client would lose connection to the VPN. DNS resolution started to fail 1 minute before the disconnect happened, almost like a warning shot.

VPN Connection error on macOS and iOS: You were disconnected because the PPP server is not responding. Try reconnecting.

For the longest time, I got mentally stuck on the idea that DNS resolution was the problem (in reality it was just a symptom). So I spent weeks searching for an answer on only that. Needless to say, I got nowhere.

The VPN server logs indicated that the keep-alive echo requests were not being answered. This was very strange and didn’t make sense.

> No response to 5 echo-requests
> Terminating on signal 15
> Serial link appears to be disconnected.

The Local IP

The biggest issue with diving into something of which one has no experience is that sometimes documentation is not helpful. This is exasperated by (personal observation) open source projects being written by volunteers who are experts in the code, but not in communication.

And I get it. I write a lot of bad documentation myself. I’ll send code out for QA and it comes back because my documentation doesn’t make sense or is even missing steps all together. I understand what I meant, my brain just doesn’t know how to translate that for someone seeing it for the first time.

As a result, in setting up the xl2tpd options, I screwed up one critical field: local ip.

In Lin’s script mentioned above, they used the subnet throughout the setup. I edited the script (which Lin recommended) to use my own subnet, and things seemed to work. But where I screwed up was a misunderstanding in what the local IP actually represented.

My understanding has always been that LAN IPs ending in .1 are usually the gateway/router. In observing Lin’s defaults, the local IP in options.xl2tpd was set to I then changed it my router’s IP also ending in .1.

But my VPN server is .3.

This resulted in the echo request going from the Server -> Client, and the response going from the Client -> Router. The server never received the “I’m still here” packets. After 5 tries, the server terminated the connection, assuming the device was not reachable.

The local ip field represents the LAN IP of the vpn server itself. If that’s also your gateway, .1 makes sense, but not in my case.

Once I changed the local ip, bam! The VPN is now stable. In fact, I turned off WiFi on my phone and left it on the VPN overnight. This morning, it was still connected.

This was the final step in achieving my post-macOS Server goals, and I’m very happy to finally move away.

Next up, two-way SSL authentication!

Feature photo by Anastasia Dulgier on Unsplash