Wireguard - changes the default route although not configured

  • Hello Team,

    Thanks for implementing wireguard in the latest release of LibreElec!

    I followed the example from here and here

    I tried to replace my site-to-site VPN from the addon tinc to wg, but somehow it keeps replacing the default route from eth0 to wg0 even though the AllowedIPs is limited to the remote subnet. If I connmanctl disconnect <vpnname> it properly restores the former default route.

    I'm not sure where to look. This is the config file ~/.config/wireguard/wireguard.config:

    I understood from the docs, that wg would only set the default route to the tunnel, if AllowedIPs would be - but it isn't.

    After connmanctl connect, the route table looks like this (the other side of the wg-end is not yet configured, so don't mind the missing route for the target subnet):

    The output of wg correctly limits to the configured target subnet.

    After "connmanctl disconnect <vpnname>" the ip route looks like this:

    ~/.config/wireguard # ip route
    default via dev eth0 dev eth0 scope link  src dev eth0 scope link via dev eth0

    I do not understand why it replaced the default route to wg0, the config file does not tell it to do so. Is this maybe a temporary state until the tunnel is established? Because that is not the case at this moment.

    Can someone give me a hint where to look?

    EDIT: I found there is a file ~/.cache/connman/<vpnname>/settings, which contains this line:


    This probably has something to do with it. I can not set it to "true", because it gets overwritten automatically by some other mechanism as soon as I "connmanctl connect <vpnname>". This mechanism probably is hardcoded to forcetunnel (route into wireguard, isn't it?

    • Official Post

    I've been told the AllowedIP setting is for internal WireGuard logic and WireGuard is not controlling the routing table. However, the WG connection is higher in the list of connman services (as it was created most recently) which means it becomes the primary service and default route. To change this we can change the service order via connmanctl, e.g.

    connmanctrl move-after vpn_138_116_14_12_wg_domain_tld ethernet_1cf05e0b93f0_cable
    Moved vpn_138_116_14_12_wg_domain_tld after ethernet_1cf05e0b93f0_cable

    So syntax is: connmanctl move-after <service_to_move> <service_to_move_it_after>

    So for scenarios where you want to connect via WireGuard to access content on a remote server, but otherwise still want/need the Internet connection to be as-normal from the local network, we can add another ExecStart in the connman.service file, e.g.

    ExecStart=/usr/bin/connmanctl connect vpn_138_116_14_12_wg_domain_tld
    ExecStart=/usr/bin/connmanctl move-after vpn_138_116_14_12_wg_domain_tld ethernet_1cf05e0b93f0_cable
    ExecStop=/usr/bin/connmanctl disconnect vpn_138_116_14_12_wg_domain_tld

    NB: I haven't tested this, but it makes sense to me, so please have a play and report back.

    NOTE: I have subsequently been told that the move-after changes are persistent, so would only need to be done once.

  • Thanks for getting back. The move-after has to be executed twice to work for me:

    As you can see, the second move-after actually changed the Interface of the default route. So I adopted the service-file:

    This way, it works. But I'm not sure why.

    Anyway, the service does not come up during boot. How can I troubleshoot this? systemctl status wireguard shows some weird errors (the wrong timestamps seem to be the case as this service gets started before the time was fixed - maybe this is also the problem here, it gets started too early?):

    If I login with root and "systemctl stop wireguard" and "systemctl start wireguard" it seems to work.

    Any ideas?

    • Official Post

    From what I've read WireGuard requires monotonic time, i.e. time can change but must always move forwards, so it shouldn't be affected by the time-jump seen when NTP kicks in (unlike TLS certs which are invalid until current time is greater than cert start dateTime) as time starts from libc compile time (some date in April?) but I'm not an expert in these things. I suspect the timing/sequencing for WireGuard in the .service file could be improved as I'm a novice with systemd things.

  • Thanks, I managed to work around with that issue by just sleeping for 5 seconds before starting with wireguard stuff upon boot. This works for me. I also tried with some other dependencies (connman.service, bluetooth.service), but that did not help. I guess I could restore the defaults there ...

    I am still wondering why this move-after has to be executed twice (for me). On the other side of the LibreElec this also not helps in every case, he sometimes need to stop / start wireguard.service several times until it works.

    • Official Post

    Having done a bit more reading I have a suspicion we need to start using systemd to handle NTP as this adds a new systemd .target for when the initial NTP sync has completed, which would be useful in this scenario. That's something for the master branch though, I wouldn't risk that kind of change being backported to 9.2 at this stage in the release lifecycle.

  • Having done a bit more reading I have a suspicion we need to start using systemd to handle NTP as this adds a new systemd .target for when the initial NTP sync has completed, which would be useful in this scenario.

    Or conman start to support /run/systemd/timesync/synchonized for reliability ...

    On RPIs (or any other devices without RTC) time-sync.target already may work today. Start enabling the needed service:

    systemctl enable systemd-time-wait-sync

    Reboot a few times and check:

    systemctl status systemd-time-wait-sync
    systemctl status time-sync.target

    If time-sync.target is always reached the dependency can be added to the wireguard unit.

  • chewitt systemd-time-wait-sync does hang randomly on Generic because ntp slew adjusts are not detected. There has to be a jump adjust in the beginning (only be used if the time error is >0.5sec).

    Assuming the initial RTC value is always "good enough" we can disable systemd-time-wait-sync on this platform with a drop-in file:

  • As far as I understand, the suggested solution needs a specific service (such as ethernet) that can be used as "move-after" target. But what happens if the ethernet cable is unplugged and the device is using wifi to access the internet? Is there a solution that works for both wifi and cable?

  • I tried to make it a bit more resilient to changes and different scenarios.

    I found it to be not reliable when started through systemd. therefore i used autoexec.py in /storage/.kodi/userdata/autoexec.py with the following content:

    from subprocess import call

    And for the actual connection if have this in /storage/connect_vpn.sh:

    Hth. A fix in connman would certainly be nicer!

    Edited once, last by nativemad ().

  • This is quite hard to use reliably. Even with nativemad's script when I'm trying to connect to my rpi from an external network using the external ip (not through wireguard), I have multiple "connmanctl move-after" commands every five seconds. This makes the connection really unstable.

    On a different but related note, I've also tried using a docker container as a wireguard client (since it works very well for a wireguard server), but apparently LibreElec doesn't include the iptables_raw kernel module so it's not possible to set AllowedIps as

    I'm not really good with networking, but is there any way to actually load this module, or to force the metric of the vpn as really high so the route will have a low priority? I'm kind of stuck here.

    Edit: In the end I just configured wireguard manually with wg. Not ideal but it works...

    Edited once, last by reyqn ().

  • After a few days I managed to solve default route issue. Below there are my scripts with descriptions.

    WG configuration:


    ListenPort = <port>
    PrivateKey = <privkey>
    PublicKey = <publkey>
    AllowedIPs =
    Endpoint = <endpoint hostname>:<port>

    Wireguard startup script:


    Stop Wireguard connection:


    ip link set down dev wg0

    Systemd service:


    Then all traffic routes via default eth0 interface and several ip's via wg0. Routes look as follows:

    At least, it works for me.