IPv6 mit der Telekom und Archlinux

Da die VDSL-Verbindung nach einer Neustiefelung meines Rechners nicht mehr starten wollte, musste ich mich mal wieder mit diesem Thema auseinandersetzen. Ursache des Ganzen war, dass adsl.service nicht mehr starten wollte, weil /usr/bin/pppoe-start nicht mehr im Paket rp-pppoe enthalten war.

PPPoE

Da immer alles funktioniert hat, ist vollkommen an mir vorbei gegangen, dass pppd mittlerweile ein Plugin bekommen hatte, das PPPoE über das entsprechende Kernel-Modul abwickelt. Scheint so, dass der RP-PPPoE ausgedient hat. Wie auch immer, um eine Verbindung mit der Telekom nur mit pppd herzustellen, erstelle man folgende Konfigurationsdatei in /etc/ppp/peers namens z. B. tkom:

plugin pppoe.so # dafür muss pppoe.ko geladen sein!
tkom # Interface name
name "<kdnr>#0001@t-online.de" # ppp-Benutzername
persist
defaultroute
+ipv6 # Entscheidend für IPv6-Prefix-Delegation!
mru 1380
mtu 1380
hide-password
noauth

Wenn man dann noch das richtige Passwort in /etc/ppp/pap-secrets hinterlegt hat, sollte sich die Verbindung mit systemctl start ppp@tkom aufbauen lassen. @tkom ist der Name der Konfigurationsdatei in /etc/ppp/peers.

Prefix Delegation

Um ein globales IPv6-Prefix für das interne Netz zu bekommen, benötigt man einen DHCPv6-Client, der es an dem PPP-Interface annimmt. Für dhcpcd sieht die Konfiguration so aus:

duid
waitip 6
ipv6only
interface ppp0
ipv6rs
iaid 1
ia_pd 1 int/0/64/801

Die letzten beiden Zeilen sind entscheidend: iaid definiert eine UUID, die dann in ia_pd (das erste Argument, in diesem Fall 1) benutzt wird, um der Netzwerkkarte mit dem Namen int ein /64-Prefix zuzuweisen. Der letzte Parameter ist das Suffix, was das Interface, an das delegiert wird, bekommt, standardmäßig 1, wenn man es auslässt. Aber Wahrschau: Das ist kein String, sondern ein Integer, der in Hexadezimal umgewandelt wird (801 == 0x321)!

Router Announcements

Um das LAN am IPv6-Leben teilhaben zu lassen, muss das IPv6-Netz auch verkündet werden, und zwar mit radvd. Um dessen Konfiguration auf Stand zu halten, gibt es den Hook /usr/lib/dhcpcd/dhcpcd-hooks/40-delegate, der /usr/local/sbin/delegate_prefix.sh aufruft, wenn es ein neues Prefix gibt. Dieses Skript schreibt eine Konfiguration mit dem neuen Prefix und startet den Trümmer neu.

DHCP

Für DNS, DHCPv4 und DHCPv6 ist dnsmasq zuständig. IPv4-DHCP ist so trivial, dass ich es nicht erklären muss.

DHCPv6 leider komplizierter. Zunächst muss man radvd mit der Option AdvManagedFlag on sagen, dass die Clients überhaupt danach fragen. Wie statische IPv6-Adressen vergeben werden, hängt von dem dem Client ab. ArchLinux mit networkd macht das recht einfach. Man muss lediglich Token=::c0a0:808 in der Sektion [IPv6AccetRA] eintragen, um ebendieses Suffix zu bekommen.

Windows hingegen fragt nach einer DUID via DHCP-SOLICIT oder INFORM. Bei dem entsprechenden dhcp-host= Eintrag beachten, dass ein vorangestelltes id: einen literalen String bedeutet. Um einen Windows-Client zu beglücken, lasse man sich die DUID via ipconfig /all ausgeben, ersetze '-' durch ':' und verteile dann eine IP ohne 'id:' nach dem Gleichheitszeichen.

Networking with Hetzner

Setting the scene

Recently I found out the hard way that Hetzner frowns upon MAC addresses on their switch ports they don’t know, so they blocked my server.

What happened: The wan interface was a member of the bridge connecting my virtual machines, because from what I knew back then that was the only way to provide the VMs with IPv6 addresses from the Hetzner subnet. So their MAC addresses (52:54:…) went out to the switch. This was so bad that they didn’t even allow me to unlock it temporarily to fix the problem!

What I did: Reboot the server via the robot to shut down. They aren’t started automatically on boot. Then notify Hetzner via the robot that I fixed the problem by reconfiguring the network configuration. They replied very quickly by checking for foreign MAC addresses and, since they found none, unlocked my server. Now I had a chance to reconfigure the network without using their hardware KVM.

The fix

First thing to do, was remove the wan interface from the bridge. That was a remnant from a completely bridged setup from long ago, anyway. Since all IP4 traffic was routed, that wasn’t a problem. IPv6 was a problem, though. The router announcements from radvd worked, but neighbor discovery didn’t, so the VMs didn’t have IPv6 connectivity.

I finally found this article: It boils down to:

  • Give the wan interface a single IPv6 address from your assigned network (netmask /128) to make it a part of it.
  • Assign the bridge the same address, only with a /64 netmask!

The article does it for Debian. Translated to systemd-networkd I ended up with this:

The wan interface

/etc/systemd/network/wan.network

[Match] 
Name=wan <- The interface name is set by an udev rule

[Network] 
Address=2001:db8::2/128 <- /128 is the clue!!!
Gateway=<replace.with.ipv4.gateway>
Gateway=fe80::1 <- This is literal!!!

[Address] 
Address=<your.ipv4.address>
Peer=<the.peer>/32

Create the bridge

[NetDev] 
Name=br0 
Kind=bridge

Do not attach the wan interface to the bridge!

Configure the bridge

[Match] 
Name=br0 

[Network] 
Address=192.168.0.1/24 <- Your private IPv4 VM network
Address=2001:db8::2/64 <- YES, this is the same address as wan!

Don’t be alarmed if br0 is not configured and down on reboot. Once you start a VM using the bridge it will be taken up and have all the right addresses. If you add the IPv6 address manually with ip, you will loose IPv6 connectivity, since the kernel creates a useless default route! You have been warned!

Works like a charm (for now)!

DDNS with bind and perl

Problem

You have a crappy internet provider who hands out dynamic IPv4 addresses and IPv6 prefixes, but you can’t remember all those numbers and want to use DNS as intended. Actually, I have 2 internet providers, Vodafone and Telekom, each with its own problems . Vodafone’s IPv6 prefix seems to be static for now, but they blessed me with DS-Lite after the update to 400 Mbit. Telekom has a dual stack, but the public IPv4 address and the delegated IPv6 prefixes are highly dynamic.

What you need

  1. A DNS zone and control over the zone file, so you can delegate a subdomain to another name server
  2. The “another name server” with a static IP. If you don’t have that, stop reading!
  3. a script for the dynamic updates

I have a root server and a DNS zone at Hetzner. The root Server has a static IPv4 address and IPv6 prefix. Also, they let you edit the pure bind zone file via a text box, not a form! Since I’m a perl guy, I did the script in my favorite perfect and easy readable language.

Configure your “another name server”

Install bind on your box with the static IP. Then generate a key for dynamic updates. Do not use dnssec-keygen as described in various tutorials. You will fail! Use ddns-confgen instead and follow the instructions in the comment. Your zone file should look something like this:

...
// THE key
key "ddns-key.dyn.d-tor.org" { 
       algorithm hmac-sha256; 
       secret "base64stufffromddns-confgen="; 
};
// THE dynamic zone
zone "dyn.d-tor.org." { 
       update-policy { grant ddns-key.dyn.d-tor.org zonesub ANY; }; 
       type master; 
       file "dyn.d-tor.org.hosts"; 
};
...

The above snippet configures the zone dyn.d-tor.org and allows updates to anyone who has THE key. Next, create the initial zone file in the directory specified by the “directory” directive in named.conf (on ArchLinux it’s the default /var/named, thus /var/name/dyn.d-tor.org.hosts):

$ORIGIN . 
$TTL 600        ; 10 minutes 
dyn.d-tor.org           IN SOA  valhalla.d-tor.org. me.d-tor.org. ( 
  1          ; serial 
  500        ; refresh (8 minutes 20 seconds) 
  500        ; retry (8 minutes 20 seconds) 
  86400      ; expire (1 day) 
  500        ; minimum (8 minutes 20 seconds) 
) 
  NS      valhalla.d-tor.org.

Then restart/reconfigure bind and check for errors:

# systemctl restart named
# journalctl -u named

If you have ip(6)tables running, allow access to port 53 UDP and TCP for good measure:

# iptables -I INPUT 2 -p udp --dport 53 -j ACCEPT
# iptables -I INPUT 2 -p tcp --dport 53 -j ACCEPT
# iptables -I INPUT 2 -p udp --dport 53 -j ACCEPT
# iptables -I INPUT 2 -p tcp --dport 53 -j ACCEPT

Copy the key to the box where you want to update from, e.g. /etc/ddns.key and set permissions to 600. It should contain only the key section:

key "ddns-key.dyn.d-tor.org" { 
       algorithm hmac-sha256; 
       secret "base64stufffromddns-confgen="; 
};

Now test it:

# nsupdate -k /etc/ddns.key <<EOF
> server 2a02:4f8:2a:254e::2
> update delete blub.dyn.d-tor.org A
> update add blub.dyn.d-tor.org 200 A 191.139.50.71
> send
> EOF

If you’ve done everything right:

# host blub.dyn.d-tor.org 2a02:4f8:2a:254e::2
blub.dyn.d-tor.org has address 191.139.50.71

Delegate the Zone

Now we need to delegate dyn.d-tor.org to 2a02:4f8:2a:254e::2. Add this to the zone file on your authoritative name server (in my case, Hetzner):

...
dyn IN NS dyndns ; delegation to dyndns.d-tor.org
dyndns IN AAAA 2a02:4f8:2a:254e::2 ; IPv6 glue record for dyndns.d-tor.org
dyndns IN A <IPv4.address.of.dyndns> ; IPv4 glue record
...

Wait for the information to spread. Check with dnstracer:

# dnstracer -s . dyn.d-tor.org

If you get a result, we can continue 🙂

THE script

Write a script that extracts the IP addresses from your interfaces and uses nsupdate to update the records. It shouldn’t be that hard. Sorry, I can’t provide mine, because it is very specific to my setup.

Multi-homed: Telekom, Vodafone and Hetzner

Problem

I refined my network configuration a bit, because there was a problem: When I pinged the Vodafone IPv6 address of hadante from my new, shiny Hetzner box, it wouldn’t answer, because I got the policy routing wrong.

Initially I routed everyting to Hetzner via the Vodafone interface, but that’s plain wrong. This way hadante even sent packets originating from Telekom IP’s via the Vodafone interface, with the Telekom IP as source. What I really wanted:

  1. answer requests to the Vodafone interface via Vodafone
  2. make Telekom the default route

Answer requests to the Vodafone IP

The solution was easy: create a rule to send everything from the Vodafone interface out there. Unfortunately, nothing is as easy as it seems. Because the Vodafone-IPv6-Prefix is semi-static, systemd-networkd policy routing doesn’t work. The routing table can be filled automatically:

[Match] 
Name=ext 

[Network] 
DHCP=yes 
IPv6Token=::dead:b0a1 

[DHCP] 
RouteMetric=4096 
RouteTable=199 

[IPv6AcceptRA] 
RouteTable=199

The RouteTable directive adds the routes acquired by DHCP and Router Announcments to the routing table 199 (aka kd, see /etc/iproute2/rt_tables), but without a rule it doesn’t do anything. The IPv6Token directive sets the IPv6 address to <prefix>::dead:b0a1, by the way.

The rule is added by a perl script written by yours truly. It does something like this (pseudo perl code):

...
$old = <old IPv6 address>;
$new = <new IPv6 address>;
# match old prefix
$old =~ m#^([[:xdigit:]]{1,4}:[[:xdigit:]]{1,4}:[[:xdigit:]]{1,4}:[[:xdigit:]]{1,4}:)#;
# delete old rule
system("/usr/bin/ip -6 rule dele from $1:/64 table kd");
# match new prefix
$new =m#^([[:xdigit:]]{1,4}:[[:xdigit:]]{1,4}:[[:xdigit:]]{1,4}:[[:xdigit:]]{1,4}:)#;
# add new rule
system("/usr/bin/ip -6 rule add from $1:/64 lookup kd");
...

This way both the Telekom IP and the Vodafone IP work from anywhere. As a bonus, IPv6 requests to Hetzner from the delegated Telekom IPv6 network now work, too 🙂

Configuring the Telekom interface

During my network configuration spree I tried to configure the Telekom interface with systemd-networkd instead of dhcpcd, but that didn’t work, unfortunately. I couldn’t get the prefix delegation to the internal interface to work. Supposedly systemd-networkd can do it, but the documentation is, let’s say, sparse at best. After several attempts I gave up and reverted to dhcpcd, as described in this post.

IPv4 to IPv6 Forwarding

Why? Imagine you have an IPv6-only VM on an IPv4 and IPv6 enabled Host, or for some reason IPv4 is routed differently on the VM than IPv6 (think VPN). Then you want to access your VM from an IPv4-only internet access point. Pretty much impossible, you’d think. But fear not! socat comes to rescue 🙂

SOcket CAT is the swiss army knife for network sockets, even when iptables can’t help you any more:

socat TCP4-LISTEN:<LPORT>,fork TCP6:[2001:db8::8]:<DPORT>

<LPORT> is the listen port on the Host. It can be anything, but if you want to run socat as a non-privileged user, it should be > 1024. <DPORT> is the destination port on the IPv6-only VM to forward <LPORT> to. Of course this is not arbitrary. The parameter fork makes socat keep listening after a connection is established. Otherwise it would exit after the connection is closed.

A systemd-unit would look like this:

[Unit]
Description=Forward Port to IPv6
Requires=sys-subsystem-net-devices-br0.device
After=sys-subsystem-net-devices-br0.device

[Service]
User=nobody
ExecStart=/usr/bin/socat TCP4-LISTEN:44444,fork TCP6:[2001:db8::8]:3389
 
[Install] 
WantedBy=multi-user.target

The Requires and After order the service after the network bridge (br0) for your VM. Change it accordingly.

If your use case would be RDP (like above), and your favorite RDP client is rdpk, you can add a Host <IPv4-Address-of-Host>:44444 to rdpk and be done! Luckily rdpk passes the Hostname verbatim to xfreerdp /v: 🙂

Telekom und IPv6 – Total tell, Todd!

Zuvörderst…

… muss man IPv6 Konnektivität herstellen. Wie das geht, habe ich in diesem Artikel beschrieben.

Wenn man aber mehr will…

… wie z. B. ein geroutetes /56er, dann muss man sich weiter anstrengen.

Das geroutete Netz bekommt man nur via DHCPv6. Am einfachsten geht das mit dhcpcd6 und einer dafür gemachten Konfiguration. Davon ausgehend, dass ppp0 das PPPoE-Interface der Telekom ist, muss sie so aussehen:

duid 
noipv6rs 
waitip 6 
ipv6only 
interface ppp0 
ipv6rs 
iaid 1 
ia_pd 1 int
  • iaid ist lediglich ein Identifier, den man referenzieren kann/muss
  • die letzte Zeile “ia_pd 1 int” ist interessant: “int” ist der Name des Netzwerk-Interfaces, dem ein Prefix zugeteilt werden soll.  Standardmäßig bekommt das Teil ein /64-Prefix mit der IP-Adresse Prefix::1/64

ACHTUNG: DHCPv6 läuft über UDP/ipv6, Port 546 ausgehend. Sonst geht gar nix! Hier die Iptables-Regel, wenn die INPUT-Policy Drop heißt:

ip6tables -I INPUT -i ppp0 -p udp -m udp --dport 546 -j ACCEPT

Wenn nix geht, zum Testen die Policies auf “ACCEPT” setzen und dann mit tcpdump schnüffeln.

Wenn man ein Prefix bekommen hat, sollte man…

RADVD installieren…

Und zwar mit folgender Config:

interface int { 
        AdvSendAdvert on; 
        MinRtrAdvInterval 3; 
        MaxRtrAdvInterval 10; 
        prefix ::/64 { 
                AdvOnLink on; 
                AdvAutonomous on; 
                AdvRouterAddr on; 
        }; 
};

“int” ist wiederum das Interface, welches das geroutete Prefix bekommen hat und irgendwie im LAN ist. Wenn an den angeschlossenen Geräten IPv6-Autokonfiguration aktiviert ist, sollten alle glücklich sein 🙂

Nachteile

Mit SLAAC (also Autokonfiguration) und NetworkManager kann zumindest im GUI keine statischen IPv6-Adressen vergeben, da hilft nur IPv4, aber das kriege ich auch noch geregelt 🙂

Hadante Routing

Well, that took quite some doing. Turns out that KabelDeutschland/Vodafone is the least worse provider for VPN-Connections. Routed via Telekom the RDP-Connections are flaky at best.

By default, everything is routed via ppp0/tkom, set up in /etc/ppp/ip-up.d/tkom-up.sh, except for valhalla and the VPN-Server@Work:

/usr/bin/ip rule add to <valhalla>/32 lookup kd
/usr/bin/ip rule add to <work>/32 lookup kd

DO NOT flush all rules, no matter what! This will inevitably lead to “Destination Host Unreachable”, because the rules for looking up main and default are flushed, too. Took me a while to figure out 🙁

To fill the routing table kd, add this to /etc/systemd/network/ext.network:

[DHCP] 
RouteMetric=4096 
RouteTable=199

This adds the routes pushed by DHCP to table 199. RouteTable 199 is defined in /etc/iproute2/rt_tables:

# 
# reserved values 
# 
255     local 
254     main 
253     default 
0       unspec 
# 
# local 
# 
#1      inr.ruhep 
200 tkom 
199 kd

Together with the rules above everything to valhalla and work is now routed via KD.

 

IPv6 mit der Telekom, Linux und pppoe

Pflicht: IPv4-Konnektivität

Wie das geht, habe ich hier beschrieben. Wenn das nicht läuft, geht auch nichts mit IPv6.

Kür: IPv6-Konnektivität

Ist eigentlich ganz einfach, wenn man weiß, dass Forwarding für das ppp-Interface ausgeschaltet sein muss. Ansonsten kann man lange auf ein Prefix warten: Man bekommt zwar eins, aber das Interface wird nicht konfiguriert!

So geht dem:

Unter Arch Linux gibt es die Datei /etc/ppp/ipv6-up.d/00-iface-config.sh. Dort trägt man Folgendes ein:

#!/bin/bash
echo 1 > /proc/sys/net/ipv6/conf/$1/use_tempaddr 
echo 0 > /proc/sys/net/ipv6/conf/$1/forwarding 
echo 1 > /proc/sys/net/ipv6/conf/$1/autoconf 
echo 1 > /proc/sys/net/ipv6/conf/$1/accept_ra

Wichtig ist die 2. Zeile: forwarding == 0, wie schon oben erwähnt. Diese Option ist der Schlüssel zum Glück, wirklich!

use_tempaddr kann ganz nach Gusto gesetzt werden, und autoconf muss natürlich auch aktiviert sein. Bei accept_ra bin ich mir nicht sicher.

Als Nächstes braucht man rdisc6 (Arch Linux: pacman -S ndisc6). Dann legt man eine neue Datei in /etc/ppp/ip-up.d an (Name egal, Hauptsache, es ist ein ausführbares Shell-Script). Bei mir heißt sie tkom-up.sh:

#!/bin/bash
rdisc6 ${IFNAME}

${IFNAME} wird von dem PPP-Gerümpel gesetzt und enthält den Namen des PPP-Interfaces (Überraschung!).

Zu guter Letzt muss man dem PPP-Dämonen noch sagen, dass er auch für IPv6 zuständig ist. Dafür fügt man die Zeile

+ipv6

irgendwo in /etc/ppp/options hinzu. Nach einem beherzten

# systemctl restart adsl

sollte eine globale IPv6-Adresse an ppp* rangeflanscht sein!

Ansonsten wäre da noch…

systemd-networkd, das standardmäßig Router-Announcements an IPv4-Only-Interfaces entgegennimmt und eine nervige Default-Route via fe80::1 setzt. Das kann man dem Trum abgewöhnen, indem man IPv6AcceptRA=false zu der .network-Unit hinzufügt. Bei mir sieht das so aus (ehemals KD, jetzt Vodafone-Verbimmelung):

[Match] 
Name=ext 
 
[Network] 
DHCP=v4 
IPv6AcceptRA=false

 

IPv6 connectivity of security.debian.org

The Problem

Have been hunting this down for quite some time now: several virtual hosts weren’t able to connect to security.debian.org. First I thought it was me, even though I had all the ingredients for IPv6-forwarding to work (this is the host):

*filter 
:FORWARD DROP [0:0]
-A FORWARD -p ipv6-icmp -j ACCEPT
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Of course, net.ipv6.conf.*.forwarding was set on the host. That should be enough to forward all outgoing connections and drop incoming, right? And it does, for pretty much any host, except security.debian.org (AKA as lobos.debian.org and villa.debian.org). There may be more, but that one caught my attention, because apt update hung just there (ftp.de.debian.org worked, btw).

First I thought that it was the MTU, but that was pretty much a red herring. After a while I realized that it was working when the FORWARD policy was ACCEPT, but of course that wasn’t a viable solution. So I dug deeper: Strangely enough, with the policy back to DROP and this rule:

-A FORWARD -d <VM-IPv6> -p tcp -m multiport \
   --sports 80,443 -j ACCEPT

it also worked, but this wasn’t enough:

-A FORWARD -s <VM-IPv6> -j ACCEPT

WTF? Fortunately I had a working virtual machine (also debian 8.6, same kernel), so I ended up comparing the IPv6-sysctl values (sysctl -a | grep ipv6).

The solution

As it turned out, the only difference was that the working virtual machine had net.ipv6.conf.*.forwarding enabled. So I added

net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1

to /etc/sysctl.conf of the failing virtual machine, rebooted and then it finally worked ™! I don’t have the slightest clue why this is necessary, though. The VM is the final receiver, the end of the chain, but certainly not a router! Maybe it’s a kernel bug, I don’t know… I’m just glad it works 🙂

Just calling sysctl -w doesn’t do it, btw. You have to take the interface down and up again to take effect, hence the reboot…

Printing troubles

In a painful, tedious quest to make my OKI B431dn actually print from a Windows VM I learned several things:

  1. First and foremost: It really, really helps if your printer doesn’t share the IPv4 address with your TV (even if it’s turned off!)
  2. Thinking that you can get the IPv6-stacks on embedded devices such as said printer to work is just wishful thinking
  3. That I (fortunately) didn’t set an admin password for my printer
  4. That my SAMSUNG TV is still online even on standby

To elaborate: My quest started, because I wanted my Windows 10 VM to print. Easy enough, you’d think, but nothing is as easy as it seems 🙁

Adventure Levels:
  1. Fight with cups and Windows and encryption (http vs. https). That was a red herring.
  2. Fight with Samba, shared printers and Windows: another red herring
  3. Fight with different drivers or PPDs
  4. Find out that printing via localhost cups is also painstakingly slow
  5. Eventually figure out that the printer shares the IP with my TV
Solution:
  • Change the IPv4-adress of the printer, turn off IPv6 and only use the (now unique) IPv4-adress.
  • Use the URLs provided by the printer web page
Remarks:

Still don’t know why printing via IPv6 didn’t work as it should, because the printer’s IPv6-address was pretty unique, but what do I know… Anyway, after applying the solution using the generic cups postscript driver and the installed windows postscript driver, printing started after seconds instead of minutes, so problem solved 🙂