NetworkManager-l2tp icon indicating copy to clipboard operation
NetworkManager-l2tp copied to clipboard

Something adds a ppp0 route to the gateway making the connection fail

Open pedrocr opened this issue 5 years ago • 31 comments

I configured a VPN using Ubuntu 19.10 and network-manager-l2tp 1.2.10. After struggling with enabling the correct phase1 and phase2 settings I now get a connection. That connection gets torn down after less than a minute.

Looking at the routing table I see that something adds a ppp0 route to the IP of the gateway I am connecting to. That breaks the communication to the gateway as that route is above the one that uses the correct wireless interface to make it continue to work. Any ideas of what could be happening?

pedrocr avatar Mar 28 '20 19:03 pedrocr

This seems like the same issue as #22 but my routing config is much simpler. It's just a simple wireless connection. Deleting that route makes the VPN not fail.

pedrocr avatar Mar 28 '20 19:03 pedrocr

Using uk.freel2tpvpn.com (i.e. 87.117.247.187) from https://www.freel2tpvpn.com/ , I have the following routes:

Before VPN connection:

$ ip route list
default via 192.168.0.1 dev eno1 proto dhcp metric 100 
192.168.0.0/24 dev eno1 proto kernel scope link src 192.168.0.74 metric 100

After VPN connection:

$ ip route list
default dev ppp0 proto static scope link metric 50 
default via 192.168.0.1 dev eno1 proto dhcp metric 100 
1.0.0.1 dev ppp0 proto kernel scope link src 10.20.0.10 metric 50 
87.117.247.187 via 192.168.0.1 dev eno1 proto static metric 100 
169.254.0.0/16 dev ppp0 scope link metric 1000 
192.168.0.0/24 dev eno1 proto kernel scope link src 192.168.0.74 metric 100 
192.168.0.1 dev eno1 proto static scope link metric 100

dkosovic avatar Mar 30 '20 12:03 dkosovic

Similarly with other L2TP/IPsec servers I connect to including my workplace, there is no route that is equivalent to the following:

route add <IP Adress of Gateway> ppp0

dkosovic avatar Mar 30 '20 13:03 dkosovic

I have the same issue. Looks like this happens when VPN address and internal point-to-point addresses are the same:

pppd[1834278]: local  IP address YY.YY.YY.YY
pppd[1834278]: remote IP address XX.XX.XX.XX
...
Data: VPN Gateway: XX.XX.XX.XX
Data: Tunnel Device: "ppp0"
Data: IPv4 configuration:
Data:   Internal Address: YY.YY.YY.YY
Data:   Internal Prefix: 32
Data:   Internal Point-to-Point Address: XX.XX.XX.XX
Data:   Static Route: XX.XX.XX.XX/32   Next Hop: 0.0.0.0
Data:   Internal DNS: ..................
Data:   Internal DNS: ..................
Data:   DNS Domain: '(none)'
Data: No IPv6 configuration
VPN plugin: state changed: started (4)

puleglot avatar Mar 31 '20 15:03 puleglot

That's very interesting. Seems like a weird VPN config if "Internal Point-to-Point Address" is the gateway address but maybe not invalid.

So is it pppd doing the wrong thing then? Or are those lines from something else?

pedrocr avatar Mar 31 '20 17:03 pedrocr

So is it pppd doing the wrong thing then?

I think pppd accepts this address from the remote peer via IPCP and then NetworkManager adds a route to it. This is a VPN on some cisco hardware btw, and Windows clients are connecting without problem.

puleglot avatar Mar 31 '20 17:03 puleglot

OK, this is what happens on my system after connecting:

$ ip r l
...
195.XX.XX.XX dev ppp0 proto kernel scope link src 10.3.3.33 metric 50 
195.XX.XX.XX via 192.168.1.1 dev p9p1 proto static metric 100 
$ ip a l
...
12: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1400 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 10.3.3.33 peer 195.XX.XX.XX/32 scope global ppp0
       valid_lft forever preferred_lft forever

where 195.XX.XX.XX is the same address everywhere and it is also a VPN gateway address (and LNS address in terms of l2tp).

puleglot avatar Mar 31 '20 17:03 puleglot

Looks exactly like what happens with this VPN as well. Doesn't the VPN fail in less than a minute from this? At least here this makes the gateway not accessible and the link tears down after only a little bit.

pedrocr avatar Mar 31 '20 18:03 pedrocr

Yes, I need to remove that bad route, otherwise the VPN dies.

puleglot avatar Mar 31 '20 18:03 puleglot

Seems like exactly the same issue then. See the /etc/network/if-up.d/ workaround to do it automatically if you haven't already.

So is this a l2tp issue or a general network-manager issue then?

pedrocr avatar Mar 31 '20 18:03 pedrocr

The following patch works for me. Maybe this can be made optional via a checkbox in GUI, something like "Ignore remote peer address"?

diff --git a/src/nm-l2tp-pppd-plugin.c b/src/nm-l2tp-pppd-plugin.c
index b7912e9..ab79772 100644
--- a/src/nm-l2tp-pppd-plugin.c
+++ b/src/nm-l2tp-pppd-plugin.c
@@ -173,11 +173,7 @@ nm_ip_up (void *data, int arg)
 	 * address up, at which point prefer the local options remote address,
 	 * and if that's not right, use the made-up address as a last resort.
 	 */
-	if (peer_opts.hisaddr && (peer_opts.hisaddr != pppd_made_up_address)) {
-		g_variant_builder_add (&builder, "{sv}",
-		                       NM_VPN_PLUGIN_IP4_CONFIG_PTP,
-		                       g_variant_new_uint32 (peer_opts.hisaddr));
-	} else if (opts.hisaddr) {
+	if (opts.hisaddr) {
 		g_variant_builder_add (&builder, "{sv}",
 		                       NM_VPN_PLUGIN_IP4_CONFIG_PTP,
 		                       g_variant_new_uint32 (opts.hisaddr));

This is how connection looks like after the patch:

18: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1400 qdisc fq_codel state UNKNOWN group default qlen 3
    link/ppp 
    inet 10.3.3.175/32 brd 10.3.3.175 scope global noprefixroute ppp0
       valid_lft forever preferred_lft forever

and it works fine.

puleglot avatar Mar 31 '20 18:03 puleglot

is there any situation where this shouldn't be done? Seems like having an option for "bork my connection if the gateway asks".

pedrocr avatar Mar 31 '20 19:03 pedrocr

is there any situation where this shouldn't be done?

I think there are configurations where remote peer address acts as a gateway. However in my case I can just add routes like ip r add XX.XX.XX.XX dev ppp0 and they will work.

puleglot avatar Mar 31 '20 19:03 puleglot

If the remote peer address that is supposed to act as the gateway is the same one that the VPN connects to in the first place that will never work.

pedrocr avatar Mar 31 '20 19:03 pedrocr

I'm not sure about adding a GUI option, ideally it should be with the rest of the routing options in the IPv4 settings which this VPN plug-in has no possibility of modifying the GUI widgets. The next likely place would be the PPP Options dialog box. The next question is how to the get the GUI setting to the nm-l2tp-ppd-plugin via the existing D-Bus interface.

Any support for GUI changes would have to be backwards compatible with other GUI frontends (e.g. KDE plasma-nm, deepin, etc).

Annoyingly all the other PPP NetworkManager VPN implementation have practically the same code for the nm_ip_up() function which adds the route, even NetworkManager itself, e.g.:

  • https://github.com/NetworkManager/NetworkManager/blob/master/src/ppp/nm-pppd-plugin.c#L197

dkosovic avatar Apr 01 '20 12:04 dkosovic

I don't think a GUI option is needed. Just not adding the route if the IPs are the same should always be the correct behavior. Unless I'm missing something.

pedrocr avatar Apr 01 '20 12:04 pedrocr

The problem is that the nm-l2tp-ppd-plugin doesn't know what the gateway address is (as far as I can tell), so it makes it difficult to compare to the server side PTP address. So the question is how to the get the gateway address to the nm-l2tp-ppd-plugin via the existing D-Bus interface or some other means.

I'm still thinking about it, but will be happy for any patch or GitHub pull request.

dkosovic avatar Apr 01 '20 23:04 dkosovic

Commit https://github.com/nm-l2tp/NetworkManager-l2tp/commit/95fdaa6dc8348ba7f63bcf7aa2ccc95b762c491d should hopefully fix this issue.

Extract from the pppd man page :

<local_IP_address>:<remote_IP_address> Set the local and/or remote interface IP addresses. Either one may be omitted. ... The remote address will be obtained from the peer if not specified in any option. Thus, in simple cases, this option is not required. If a local and/or remote IP address is specified with this option, pppd will not accept a different value from the peer in the IPCP negotiation, unless the ipcp-accept-local and/or ipcp-accept-remote options are given, respectively.

ipcp-accept-remote With this option, pppd will accept the peer's idea of its (remote) IP address, even if the remote IP address was specified in an option.

I used :<gateway_IP_address> and ipcp-accept-remote in the generated ppp options file so that <gateway_IP_address> could be passed to nm-l2tp-ppd-plugin where I could make sure the broken route to the <gateway_IP_address> is no longer added.

I deleted some comments in this issue as they weren't adding anything and to make it easier to read the actual issue.

This fix will be in the new NetworkManager-l2tp 1.8.4 which I hope to release within the next couple of weeks.

dkosovic avatar Sep 25 '20 23:09 dkosovic

@dkosovic, I am experiencing this issue too (additional route). I use Arch Linux, however, networkmanager-l2tp is out of date (1.20.0-3), therefore I wanted to test it out using the PKGBUILD below.

PKGBUILD
pkgname=networkmanager-l2tp-1.20.4
pkgver=1.20.4
_pppver=2.4.9
pkgrel=2
pkgdesc='L2TP support for NetworkManager'
arch=(x86_64)
url='https://github.com/nm-l2tp/NetworkManager-l2tp'
license=(GPL2)
depends=(libnma libsecret openssl "ppp=$_pppver" xl2tpd)  # aur
makedepends=(intltool python git)
optdepends=('strongswan: IPSec support')
provides=("networkmanager-l2tp=$pkgver")
conflicts=(networkmanager-l2tp networkmanager-l2tp-git)
source=("git+$url#tag=1.20.4")
sha256sums=('SKIP')

prepare() {
  ln -sf NetworkManager-l2tp $pkgname
  cd $pkgname
  NOCONFIGURE=1 ./autogen.sh
}

build() {
  cd $pkgname
  ./configure \
    --libexecdir=/usr/lib/NetworkManager \
    --localstatedir=/var \
    --prefix=/usr \
    --sysconfdir=/etc \
    --with-pppd-plugin-dir=/usr/lib/pppd/$_pppver
  sed -i -e 's/ -shared / -Wl,-O1,--as-needed\0/g' libtool
  make
}

package() {
  make -C $pkgname DESTDIR="$pkgdir" install
  install -Dm644 $pkgname/NEWS "$pkgdir/usr/share/doc/$pkgname/NEWS"
}

The additional route ($vpn_ip dev ppp0 proto kernel scope link src $remote_ip) is still created.

Is this issue fixed? Is there anything else I am missing?

Thanks! :pray:

Update: How to use :<gateway_IP_address> within nmcli? Is there a way to configure this for NetworkManager connections?

tukusejssirs avatar May 09 '22 19:05 tukusejssirs

For the Arch Linux PKGBUILD (and unrelated to the routing issue) I would recommend the following changes :

  • Add --with-gtk4 configure switch, otherwise can not edit connections with gtk4 based gnome-control-center.
  • Add --enable-libreswan-dh2 configure switch as AUR libreswan package is built with DH2 (modp1024) support.
  • Remove intltool from the make depends as it hasn't been required since commit https://github.com/nm-l2tp/NetworkManager-l2tp/commit/219123de661dc16eb4f79b7ca7476fdc63a839d3

NetworkManager 1.36 had a major overhaul of the way it handles IP configurations internally, including routing.

The issue you are experiencing is not the same, but similar, the original was for a ppp0 metric 50 route, the current one is regarding a ppp0 metric 0 (or no metric) route as described in the following NetworkManager >= 1.36 issue:

  • https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/961

I originally closed this issue with commit https://github.com/nm-l2tp/NetworkManager-l2tp/commit/95fdaa6dc8348ba7f63bcf7aa2ccc95b762c491d (and there was a code clean-up commit https://github.com/nm-l2tp/NetworkManager-l2tp/commit/906c0a4923515f967ff2555d815bb62c8745d6e0), but now think this was the completely wrong thing to do as all it did was prevent telling NetworkManager what the peer-to-peer host was (i.e. NM_VPN_PLUGIN_IP4_CONFIG_PTP). The routing fix should have been in NetworkManager code where the route shouldn't have been added if NM_VPN_PLUGIN_IP4_CONFIG_PTP and NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY were the same.

Unfortunately if upstream NetworkManager incorporates a fix to not add the route if NM_VPN_PLUGIN_IP4_CONFIG_PTP and NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY are the same, I will need to release a new NetworkManager-l2tp that undoes the commit that originally fixed this issue.

As a workaround, you can delete the spurious ppp0 metric 0 route in /etc/ppp/ip-up.d/01-routes.sh using the ip route del command, much like the ip route add is doing in the following example : https://wiki.archlinux.org/title/PPTP_Client#Split_Tunneling

Sorry I'm not sure I understand your :<gateway_IP_address> within nmcli question.

dkosovic avatar May 09 '22 21:05 dkosovic

Actually, I suspect you won't be able to have an ip route del line in /etc/ppp/ip-up.d/01-routes.sh to delete the problematic route as it might be too early to do so. Sorry I don't have any suggestions

dkosovic avatar May 10 '22 10:05 dkosovic

Thanks, @dkosovic, for your help! :pray:

Is --enable-libreswan-dh2 needed when I use strongswan?

The issue you are experiencing is not the same, but similar

Oops, sorry then for raising this issue here.

My problem lays in the additional route: unless I delete it, I can’t connect noly only to the VPN network, but also to the Internet (or at least the Internet connection is severly limited). If I run ip r del $vpn_ip right after bringing up the VPN connection (nmcli c up vpn), everything works as expected.

# Note: IPs were redacted.
default dev ppp0 proto static scope link metric 50
default via 192.168.1.1 dev wlan0 proto dhcp metric 60
192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.31 metric 60
192.168.1.1 dev wlan0 proto static scope link metric 50
1.2.3.4 dev ppp0 proto kernel scope link src 4.3.2.1  # <<<
1.2.3.4 via 192.168.1.1 dev wlan0 proto static metric 50

I think I should add a comment about my issue on NetworkManager issue tracker.

tukusejssirs avatar May 10 '22 11:05 tukusejssirs

The --enable-libreswan-dh2 configure switch was just a general comment about issues I saw with the PKGBUILD, it along with the others have zero impact if you are using strongswan.

strongswan just work for you with networkmanager >=1.36,? You didn't need to stop loading broken strongswan plugins as described in the following ?

  • https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/946#note_1350911
  • https://bugs.archlinux.org/task/74553?project=5&string=strongswan

dkosovic avatar May 11 '22 02:05 dkosovic

Well, it didn’t until this week, because this week I found somewhere on the Internet that removing the extra route (the one without explicit metric) allows me to connect to the VPN network and to the Internet.

I created a NM connection like this if it matters:

nmcli c add con-name "$con_name" type vpn vpn-type l2tp vpn.data 'gateway=1.2.3.4, ipsec-enabled=yes, ipsec-psk=p5kp4ss, password-flags=0, user=username' vpn.secrets 'password=us3rp4ss'

I didn’t configure anything related to NM, but I use EndeavourOS (Atlantis; installed in December 2021), so it could set up something. /etc/NetworkManager/NetworkManager.conf is empty and there are no files in /etc/NetworkManager/conf.d/.

Current versions I use:

If you need to know anything else, I’ll provide any information you need. :wink:

tukusejssirs avatar May 11 '22 07:05 tukusejssirs

@tukusejssirs , thanks for all that and appreciate that you added a comment upstream to the NetworkManager routing issue.

I've never used nmcli like that!

I totally forgot with the strongswan plugin issue, I had to enable the "Use this connection only for resources on its network" checkbox in the IPv4 settings (which corresponds to the ipv4.never-default setting) to reproduce the issue. So I think that explains why you weren't impacted by it.

I'll leave this issue open if others are affected by the extra route with NetworkManager >= 1.36.

dkosovic avatar May 11 '22 11:05 dkosovic

@dkosovic, do you think there is a NM config not to create the extra route? Like one of the following:

ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)
ipv4.routing-rules:                     --
ipv4.ignore-auto-routes:                no
IP4.ROUTE[1]:                           dst = 0.0.0.0/0, nh = 0.0.0.0, mt = 50

Here is full config with IPs and passwords redacted:

VPN config
connection.id:                          vpn_name
connection.uuid:                        fd71468c-19c6-4276-95bb-e15b055012c3
connection.stable-id:                   --
connection.type:                        vpn
connection.interface-name:              --
connection.autoconnect:                 yes
connection.autoconnect-priority:        0
connection.autoconnect-retries:         -1 (default)
connection.multi-connect:               0 (default)
connection.auth-retries:                -1
connection.timestamp:                   1652266868
connection.read-only:                   no
connection.permissions:                 --
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --
connection.autoconnect-slaves:          -1 (default)
connection.secondaries:                 --
connection.gateway-ping-timeout:        0
connection.metered:                     unknown
connection.lldp:                        default
connection.mdns:                        -1 (default)
connection.llmnr:                       -1 (default)
connection.dns-over-tls:                -1 (default)
connection.wait-device-timeout:         -1
ipv4.method:                            auto
ipv4.dns:                               --
ipv4.dns-search:                        --
ipv4.dns-options:                       --
ipv4.dns-priority:                      0
ipv4.addresses:                         --
ipv4.gateway:                           --
ipv4.routes:                            --
ipv4.route-metric:                      -1
ipv4.route-table:                       0 (unspec)
ipv4.routing-rules:                     --
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   no
ipv4.dhcp-client-id:                    --
ipv4.dhcp-iaid:                         --
ipv4.dhcp-timeout:                      0 (default)
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.dhcp-fqdn:                         --
ipv4.dhcp-hostname-flags:               0x0 (none)
ipv4.never-default:                     no
ipv4.may-fail:                          yes
ipv4.required-timeout:                  -1 (default)
ipv4.dad-timeout:                       -1 (default)
ipv4.dhcp-vendor-class-identifier:      --
ipv4.dhcp-reject-servers:               --
ipv6.method:                            auto
ipv6.dns:                               --
ipv6.dns-search:                        --
ipv6.dns-options:                       --
ipv6.dns-priority:                      0
ipv6.addresses:                         --
ipv6.gateway:                           --
ipv6.routes:                            --
ipv6.route-metric:                      -1
ipv6.route-table:                       0 (unspec)
ipv6.routing-rules:                     --
ipv6.ignore-auto-routes:                no
ipv6.ignore-auto-dns:                   no
ipv6.never-default:                     no
ipv6.may-fail:                          yes
ipv6.required-timeout:                  -1 (default)
ipv6.ip6-privacy:                       -1 (unknown)
ipv6.addr-gen-mode:                     stable-privacy
ipv6.ra-timeout:                        0 (default)
ipv6.dhcp-duid:                         --
ipv6.dhcp-iaid:                         --
ipv6.dhcp-timeout:                      0 (default)
ipv6.dhcp-send-hostname:                yes
ipv6.dhcp-hostname:                     --
ipv6.dhcp-hostname-flags:               0x0 (none)
ipv6.token:                             --
vpn.service-type:                       org.freedesktop.NetworkManager.l2tp
vpn.user-name:                          --
vpn.data:                               gateway = 1.2.3.4, ipsec-enabled = yes, ipsec-psk = psk, password-flags = 0, user = pass
vpn.secrets:                            <hidden>
vpn.persistent:                         no
vpn.timeout:                            0
proxy.method:                           none
proxy.browser-only:                     no
proxy.pac-url:                          --
proxy.pac-script:                       --
GENERAL.NAME:                           vpn_name
GENERAL.UUID:                           fd71468c-19c6-4276-95bb-e15b055012c3
GENERAL.DEVICES:                        wlan0
GENERAL.IP-IFACE:                       wlan0
GENERAL.STATE:                          activated
GENERAL.DEFAULT:                        yes
GENERAL.DEFAULT6:                       no
GENERAL.SPEC-OBJECT:                    /org/freedesktop/NetworkManager/ActiveConnection/1
GENERAL.VPN:                            yes
GENERAL.DBUS-PATH:                      /org/freedesktop/NetworkManager/ActiveConnection/13
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/Settings/8
GENERAL.ZONE:                           --
GENERAL.MASTER-PATH:                    /org/freedesktop/NetworkManager/Devices/3
IP4.ADDRESS[1]:                         4.3.2.1/24
IP4.ADDRESS[2]:                         4.3.2.1/24
IP4.GATEWAY:                            0.0.0.0
IP4.ROUTE[1]:                           dst = 0.0.0.0/0, nh = 0.0.0.0, mt = 50
IP6.GATEWAY:                            --
VPN.TYPE:                               l2tp
VPN.USERNAME:                           username
VPN.GATEWAY:                            1.2.3.4
VPN.BANNER:                             --
VPN.VPN-STATE:                          5 - VPN connected
VPN.CFG[1]:                             gateway = 1.2.3.4
VPN.CFG[2]:                             ipsec-enabled = yes
VPN.CFG[3]:                             ipsec-psk = vpn
VPN.CFG[4]:                             password-flags = 0
VPN.CFG[5]:                             user = pass

tukusejssirs avatar May 11 '22 11:05 tukusejssirs

ipv4.ignore-auto-routes should not create that extra route, but you will probably need to add the missing routes that are normally automatically added.

In the following issue, ipv4.ignore-auto-routes got turned on with NetworkManager 1.36.0 to 1.36.2 when a connection was edited or added and it caused issues for some :

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/946#note_1350911

dkosovic avatar May 11 '22 11:05 dkosovic

I have re-created the NM connection for this VPN a couple of times this week, therefore ipv4.ignore-auto-routes is not turned on for me. :man_shrugging:

As for turning it to on and to add the routes myself: from my point of view, it does not matter if I remove a single route or add another one. For me a solution would not require any changes to the routes after turning on the NM connection. It does not matter if I configure something else (NM, NM-l2tp, …).

What is the ipv4.routing-rules for could I pre-define the routes there and enable ipv4.ignore-auto-routes? Could that work this issue around?

tukusejssirs avatar May 11 '22 11:05 tukusejssirs

Sorry didn't mean /etc/network/if-up.d, I meant add the routes in /etc/ppp/ip-up.d/01-routes.sh where you can also add conditionals case statements like the following :

#!/bin/bash

# This script is called with the following arguments:
# Arg Name
# $1 Interface name
# $2 The tty
# $3 The link speed
# $4 Local IP address for the interface
# $5 Peer IP address
# $6 Optional 'ipparam' parameter specified to pppd

case "$5" in
    172.16.244.*)
        ip route add 172.16.244.0/24 dev $1
    ;;
esac

dkosovic avatar May 11 '22 12:05 dkosovic

Thanks, @dkosovic, for the clues! :pray:

It was quite easy to work this issue around, once I found the docs and tried a few configurations. ipv4.routes accepts comma-separated list of route definitions (src). For me, it was enough to provide ipv4.routes "$vpn_ip/$cidr $router_ip", as the VPN server has a static IP address and I don’t need any special config. You might want to try to list all the server IPs if it is finite list and all of them have static IP addresses.

Also note that setting ipv4.ignore-auto-routes yes didn’t do anything when I set it along with ipv4.routes (the route specified in ipv4.routes was simply added to the default routes, but with one distinction: instead of my physical ifname like wlan0 or eth0, the route added by ipv4.routes had ppp0, which didn’t work).

So, the following command works for me:

nmcli c add con-name "$con_name" type vpn vpn-type l2tp \
  vpn.data "gateway=$vpn_ip, ipsec-enabled=yes, ipsec-psk=$psk, password-flags=0, user=$user" \
  vpn.secrets "password=$pass" ipv4.routes "$vpn_ip/$cidr $router_ip" ipv4.never-default yes

Update

I rejoiced too soon. I enabled the wrong connection, therefore the command above still produces the default routes and the one with ifname set to ppp0.

$vpn_ip dev ppp0 proto kernel scope link src $remote_ip
$vpn_ip via $router_ip dev wlan0 proto static metric 50  # only this one is needed
$vpn_ip via $router_ip dev ppp0 proto static metric 50  # added by `ipv4.routes`

tukusejssirs avatar May 11 '22 13:05 tukusejssirs