L2TP Client: OpenBSD

Back up to the Incoming L2TP Category
From AAISP Support Site


Notes from a customer, January 2025. (Thank you!)

I've worked out how to make use of AA's L2TP service from OpenBSD 7.6, at least for IPv4. (I'm not yet certain of how or whether IPv6 is possible.) I can first offer basic instructions akin to the "Linux / xl2tpd" section of this page: https://support.aa.net.uk/L2TP_Client:_Linux . These instructions assume that you're not using ppp for anything else on your OpenBSD machine.

while preparing this message that I noticed this thread. Oh well, it seems to confirm that ipv6 is not an option, at least not as of 2021.


1. Install xl2tpd

$ doas pkg-add xl2tpd

2. Add these configuration details to /etc/xl2tpd/xl2tpd.conf:

; Configuration for AAISP L2TP service
[lac aaisp]
lns = l2tp.aaisp.net.uk
require authentication = no
pppoptfile = /etc/ppp/options

3. Add the _xl2tpd user to the network group.

I edited /etc/group manually, but it looks like this usermod invocation should do it:

$ doas usermod -G network _xl2tpd

4. Place these options in /etc/ppp/options.

(Unfortunately you cannot use an aanet-specific options file (as far as I know) because the options we will specify here would require pppd to be invoked as root, and xl2tpd will be running it as the _xl2tpd user.)

noauth
user <AA L2TP service username here>
persist

The `persist` option indicates that pppd should try to restart the connection if the connection drops. You may wish to add additional options for resilience like `maxfail 0`; see the Debian L2TP fallback instructions for suggestions, but beware that several listed there (e.g. +ipv6) will not work.

5. Change group and permissions for the options file

so that users in the network group can read it.

$ doas chgrp network /etc/ppp/options
$ doas chmod g+r /etc/ppp/options

6. Add authentication details to /etc/ppp/chap-secrets.

You'll need to add a line with four whitespace-separated fields, containing your L2TP service username, either the L2TP server name ("B.Careless") or just * since we're assuming you don't plan on using ppp for anything else, the L2TP password, and then *. You could use this command:

$ doas sh -c 'echo "<username>\t*\t<password>\t*" >> /etc/ppp/chap-secrets'

7. Create an interface configuration file for the PPP connection.

$ doas sh -c 'echo down > /etc/hostname.ppp0'

You can then run `doas sh /etc/netstart` to activate the ppp0 interface (or you could reboot if you prefer), then use `ifconfig` to confirm the presence of ppp0.

8. Finally, enable and start the xl2tpd daemon.

$ doas rcctl enable xl2tpd
$ doas rcctl start xl2tpd

From this point, if you wish to bring up your L2TP tunnel, you can

$ doas sh -c 'echo "c aaisp" > /var/run/xl2tpd/l2tp-control'

You can bring it down again with

$ doas sh -c 'echo "d aaisp" > /var/run/xl2tpd/l2tp-control'

As the generic L2TP Linux instructions note: "you will then need to figure out what you want to route up the tunnel."


Miscellaneous notes:

As suggested here, you also may wish to modify your crontab so that your computer establishes the tunnel automatically.

If you'd like your OpenBSD machine to preferentially route traffic to and from the tunnel automatically, one way to do it uses ifstated to establish and tear down routes whenever it notices that the tunnel is established. An approach which is too clever by half takes advantage of ifstated's state machine mechanism to reduce susceptibility to corner cases and race conditions, or at least that's the intention.

1. Create /etc/ifstated.conf

with contents like these:

# 20-second poll to see if the tunnel is established.
tunnel_if_up = '("ifconfig ppp0 | fgrep -q inet" every 20)'
# 20-second poll to see whether traffic is routed through the tunnel.
tunnel_rt_up = '("route -n show | fgrep default | fgrep -q ppp0" every 20)'

# Start state. The state of the tunnel isn't known.
# Assess the situation and jump to the appropriate state.
state start {
        if $tunnel_if_up {
                if   $tunnel_rt_up {
                        set-state tunnel_up
                }
                if ! $tunnel_rt_up {
                        set-state tunnel_bring_up
                }
        }
        if ! $tunnel_if_up {
                if   $tunnel_rt_up {
                        set-state tunnel_bring_down
                }
                if ! $tunnel_rt_up {
                        set-state tunnel_down
                }
        }
}

# The tunnel is up but traffic isn't being routed through it.
# Route general traffic through the tunnel.
# Note that before we do that, we specify a route to the remote L2TP endpoint
# through our own router; otherwise we'd try to send the messages that    
# implement the tunnel through the tunnel, which obviously cannot work.
# Once routing is established, advance to the "tunnel_up" state.
state tunnel_bring_up {
        init {
                run "logger -st ifstated 'establishing route through tunnel'"
                run "route add -inet -host -priority 8 194.4.172.12 <local router gateway>"
                run "route change -inet default 81.187.81.187"
        }
        if   $tunnel_rt_up {
                set-state tunnel_up
        }
}
# The tunnel is up. If it goes down, jump to the "tunnel_bring_down" state.
state tunnel_up {
        if ! $tunnel_if_up {
                set-state tunnel_bring_down
        }
}

# The tunnel has gone down, but we (may) still have routes for the tunnel in
# place. Remove them and jump to the "tunnel_down" state.
state tunnel_bring_down {
        init {
                run "logger -st ifstated 'disestablishing route through tunnel'"
                run "dhcpleasectl <local network interface>"
                run "route del 194.4.172.12"
        }
        if ! $tunnel_rt_up {
                set-state tunnel_down
        }
}
# The tunnel is down. If it comes back up, jump to the "tunnel_bring_up" state
# to restore routes through it.
state tunnel_down {
        if $tunnel_if_up {
                set-state tunnel_bring_up
        }
}

The script specifies several IP addresses that may be specific to the L2TP service that AA are providing to me (e.g. 81.187.81.187, the gateway at the AA end of the tunnel). Also specified is 194.4.172.12, one of several addresses bound to l2tp.aa.net.uk: AA's L2TP support page discourages you from specifying an IP address instead of l2tp.aa.net.uk, but if you use this FQDN in both xl2tpd.conf and ifstated.conf, then you run the risk of xl2tpd choosing one of the IPs for that FQDN and the route command in this config file using another. (So, if you use ifstated, you may want to stick to one of the IPs and change xl2tpd.conf to use that same IP as well. Keep an eye on where l2tp.aa.net.uk points now and then in case your chosen IP falls out of use.)

This script also assumes that your normal internet connection is configured via dhcp, hence it invokes `dhcpleasectl` to restore ordinary routing when the tunnel is down.

2. Enable and start ifstated.

$ doas rcctl enable ifstated
$ doas rcctl start ifstated