L2TPv3 in Linux Using IPv6 Endpoints

Pseudowires have traditionally been deployed in ISP and wireless provider networks to carry Ethernet and TDM frames across an IP/MPLS network. Now you can find an implementation of L2TPv3 in the Linux kernel. Pseudowires for the masses without the need for an MPLS network! You get the added benefit of open source code that can be modified to meet requirements specific to your environment.

L2TPv3 is a lightweight protocol for transporting L2 frames across an IP network (see RFC3931). Cisco’s implementation can carry a variety of L2 protocols (ATM, FR, Ethernet, TDM) while Linux supports Ethernet and PPP. I have use cases in mind in the Iaas space, so Ethernet pseudowires will do the trick. The linux implementation support static tunnels only. If you want an L2TPv3 control channel, check out Katalix’s commercial software, ProL2TP.

Some bad news–L2TPv3 will not work out-of-the box with any of the major Linux distributions (This holds true as of Sept 2013 at least).  You need a more recent version of iproute2 that provides the “ip l2tp” configuration command. If you want IPv6 endpoints, you’ll also need kernel 3.5 or later.

I’ll step through an example of getting L2TPv3 to function with IPv6 in a debian wheezy VM. I’m using a 32-bit Debian 7.1 image.

Install Required Packages

roote@debian:~# apt-get install flex bison libdb5.1-dev build-essential kernel-package bridge-utils

Install iproute2

Download iproute2 from this link. I linked to version 3.11, the latest at the time of this writing. Earlier versions may work. I can’t determine where this patch to handle IPv6 endpoints got merged with the code.

Execute ./configure and ignore the error about xtables. Run ‘make’ to compile. I renamed /sbin/ip to /sbin/ip-ss120521. I moved the new file in iproute2-3.11.0/ip/ip to /sbin/ip. If you want to put the new ip in /usr/local/sbin, you can do that instead. I got burned doing this because I didn’t realize that bash retains a hash to matching binaries in your path. Logging out and logging back in is one way to fix this.

Run ‘ip -V’ to ensure you using the correct binary. With iproute2-3.11.0, you’ll see ‘ip utility, iproute2-ss130903’.

Compile 3.5 or Later Kernel

The kernel compilation can be very slow. I set the VM memory to 2Gb with two processors.

root@debian:~# wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.11.1.tar.xz

root@debian:~# tar xf linux-3.11.1.tar.xz

root@debian:~# cd linux-3.11.1

root@debian:~/linux-3.11.1# cat /boot/config-`uname -r`>.config

root@debian:~/linux-3.11.1# yes “” | make oldconfig

root@debian:~/linux-3.11.1# make-kpkg clean

root@debian:~/linux-3.11.1# time fakeroot make-kpkg –initrd –revision=3.5.0 –append-to-version=-1custom kernel_image kernel_headers

root@debian:~/linux-3.11.1# dpkg -i ../linux-*1custom*

root@debian:~/linux-3.11.1# shutdown -r now

Note: I borrowed most of the compilation steps from lindqvist’s blog. I’ll also point out that you can edit .config to build L2TPv3 into the kernel rather than as a loadable module.

You’ll want to take a snapshot of this VM and make clones based on it. The next step assumes you have two Debian VMs with L2TPv3.

Build L2TPv3 Tunnels

Let’s assume you have two VMs. The interface eth0 on both is a shared network (2001:DB8::/64). The eth1 interface on both connect to the networks you want bridged together. Since the transport is IP, there is no need for a shared network. The remote endpoint could be any node that is reachable by IP.


root@debian:~# modprobe l2tp_eth

root@debian:~# ip l2tp add tunnel tunnel_id 3000 peer_tunnel_id 4000 encap udp local 2001:DB8::1 remote 2001:DB8::2 udp_sport 5000 udp_dport 6000

root@debian:~# ip l2tp add session tunnel_id 3000 session_id 1000 peer_session_id 2000

root@debian:~# ip link set l2tpeth0 up mtu 1488

root@debian:~# brctl addbr br0

root@debian:~# brctl addif br0 l2tpeth0

root@debian:~# brctl addif br0 eth1 ip link set br0 up promisc on


root@debian:~# modprobe l2tp_eth

root@debian:~# ip l2tp add tunnel tunnel_id 4000 peer_tunnel_id 3000 encap udp local 2001:DB8::2 remote 2001:DB8::1 udp_sport 6000 udp_dport 5000

root@debian:~# ip l2tp add session tunnel_id 4000 session_id 2000 peer_session_id 1000

root@debian:~# ip link set l2tpeth0 up mtu 1488

root@debian:~# brctl addbr br0

root@debian:~# brctl addif br0 l2tpeth0

root@debian:~# brctl addif br0 eth1 ip link set br0 up promisc on

You can verify the tunnel with ‘ip l2tp show tunnel’. It should look like the following.

root@debian:~# ip l2tp show tunnel
Tunnel 4000, encap UDP
From 2001:db8::2 to 2001:db8::1
Peer tunnel 3000
UDP source / dest ports: 6000/5000

Now you have a pseudowire between the networks connected to eth1 of both VMs.

If you have any problems while following my directions, let me know in the comments. I will make corrections. My thanks go to James Chapman and his peers at Katalix for writing the code and providing pointers on getting it working.


IPv6 in Ubuntu Natty Narwhal

I always considered Linux to be a leader in IPv6; the first IPv6 code was added to the kernel in 1996. I’ve recently noticed that the several Linux distributions do not support IPv6 in a way that allows typical users to connect to an IPv6 network. Let’s examine Ubuntu Natty Narwhal.

Natty does not activate IPv6 by default and requires special configuration. Windows 7 has better IPv6 support out of the box than Ubuntu Natty Narwhal. Surprising? An average user can obtain connectivity to the IPv6 Internet using default configuration in Windows 7.

Activating IPv6 in Natty can be done using NetworkManager  (GUI tool) or  manually editing /etc/network/interfaces. There are limitations. Even though the ISC DHCP client supports DHCPv6, the end station will not request a nameserver. For /etc/network/interfaces, you’d think that obtaining an IPv6 address through DHCPv6 would be simple: add an “iface ethx inet6 dhcp” line. Unfortunately, this won’t work in Natty. I am using a hack. I assign a ULA address and then use post-up to execute dhclient.

iface eth0 inet6 static
address fc00::1
netmask 64
post-up /sbin/dhclient -6 eth0

I figured this out through trial and error; this method is not fitting for the average user.

The next Ubuntu release is Oneiric Ocelot. IPv6 should be enabled by default in it. I am testing an alpha build that doesn’t yet have a fix for enabling DHCPv6 in /etc/network/interfaces. Let’s hope this gets fixed prior to release.

After encountering problems, I found that Tore Anderson opened bugs in April 2011. I recommend reading Tore’s exchange with the developer on enabling IPv6 by default. Tore tries to correct the mindset that IPv6 is only needed for power users.

The thread is here.

The bug for the missing DNS server in DHCPv6 is here.