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.

VM1

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

VM2

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

root@debian:~#

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.