VPNs dienen der geschützten Kommunikation, entsprechend einfach sollte ihre Einrichtung sein. Unglücklicherweise ist aber schon die Festlegung, was genau schützenswert ist, Ergebnis einer komplexen Abstimmung. Deswegen werden zunehmend VPNs global aufgemacht und nur bei Bedarf genutzt. Ein striktes Gerät, wie die ASA, mag das gar nicht. Ein Endkundengerät lieb es …

IPSec klassisch

Der klassische Ansatz ist es, für jedes Paar von geschützten Netzen einen Tunnel anzulegen, der je nach Bedarf aufgebaut und abgebaut werden kann. Diese Tunnel nennt man Security Association, sie definieren sich über die IP-Adressen der beteiligten Netze.

vpn-policy

Durch die dynamischen Tunnel muss jedes beteiligte Gerät wissen, welche Paare von Netzen zu schützen sind und notfalls einen Tunnelaufbau initiieren. Es ist unzulässig, Pakete unverschlüsselt zu übertragen, wenn die Policy Verschlüsselung vorschreibt.

Man kann eine solche Policy also auch so verstehen, das es eine ACL gibt, die festlegt, welcher Datenverkehr verschlüsselt ist oder nicht. Konsequenterweise enthalten die Tunnel dann auch Protokoll und Portnummern, wenn nicht rein auf IP Ebene selektiert wird.

Umgekehrt wird die Policy-ACL auch bei eingehendem Verkehr angewendet. Was laut Policy verschlüsselt zu sein hat, muss verschlüsselt ankommen. Pakete, die am VPN vorbei eintrudeln, werden kommentarlos verworfen.

Das offenkundige Problem mit dem Ansatz ist der Koordinierungsaufwand:

  • Beide Seiten müssen sich über die exakten Policy-ACLs klar sein.
  • Änderungen sind nur beidseitig und gleichzeitig möglich.

IPSec simplified

Die offenkundige Verbesserung besteht darin, dem Tunnel nicht mehr mit den IP-Adressen zu verknüpfen. Stattdessen wird ein generischer Tunnel aufgebaut, der beliebige Datenpakete verschlüsselt transportieren kann.

Jede Seite legt für sich fest, welche Ziele durch den Tunnel und welche unverschlüsselt übertragen werden sollen.

vpn-routing

Da man die Entwicklung eines neuen Protokolls scheute, wird ein klassischer IPSec Tunnel genutzt, der jedoch maximal breit arbeitet: Beliebige Quell- und Ziel-Adressen passen in den Tunnel.

Selbstverständlich kann man nun nicht mehr mit (Policy-)ACLs arbeiten, denn dann müsste sämtlicher Datenverkehr zwangsweise in den Tunnel geschoben werden. Stattdessen verzichtet man komplett auf die ACLs und routet die Ziel-Adressen entweder in den Tunnel oder ins Internet.

Auf diese Weise können Tunnel nach Bedarf auf jeder Seite einzeln konfiguriert werden: Es gibt einen existenten Tunnel und da wirft man rein, was wichtig scheint. Die Gegenseite muss nicht mehr informiert werden.

Eine Konsequenz aus dem Verzicht auf die ACLs, ja auf den Verzicht auf Abstimmung, ist, dass das empfangende Gerät nicht mehr feststellen kann, ob das ankommende Paket verschlüsselt hätte sein sollen oder nicht. Bei Fehlkonfigurationen kann ein Teil des Traffics unverschlüsselt laufen.

vpn-routing-fail

Die Vorteile liegen auf der Hand:

  • Einfaches Setup (nur Gegenstelle und Schlüssel notwendig)
  • Verschlüsselung nach Bedarf (des Senders) beliebig erweiterbar
  • Bei Fehlkonfiguration bleibt die Funktion bestehen, nur die Sicherheit ist weg.

Mischbetrieb

Der vereinfachte Ansatz ist gerade im Endkundenbereich beliebt, weil sich der Kunde so schnell seine Lan-Kopplung zusammen klicken kann. Der striktere Ansatz ist eher im Firmenumfeld zu finden, wo man traditionell nur mit abgestimmten Konfigurationen zu tun hat.

In meinem Fall habe ich nun ein Endkundengerät (CPE), das ausschließlich nach der vereinfachten Methode arbeitet, und eine ASA, die ausschließlich nach der traditionellen Methode konfiguriert werden will.

vpn-mixed-fail

Aufgrund der Policy-ACL würde eine ASA keinen unverschlüsselten Traffic annehmen, wenn der durch den Tunnel kommen müsste. Das will ich nicht aufgeben.

An der CPE sind die Wechselmöglichkeiten zu beschränkt, um überhaupt einen anderen Typ von Verbindung hinzubekommen. Also konzentriere ich mich auf das eigentliche Problem: Den VPN Tunnel und die dazu passende Policy-ACL.

vpn-mixed

Da die CPE den Tunnel vorgibt, muss er offensichtlich ein 0.0.0.0/0.0.0.0 Tunnel werden. Das führt zur sofortigen Funktionsunfähigkeit der ASA.

Schaut man genauer auf die ACLs, so stellt man fest, dass diese mehrfach benutzt werden:

  • Jedes Paket, dass von der ACL erlaubt wird, kommt in den Tunnel oder muss daraus kommen.
  • Jedes Paket, dass von der ACL abgelehnt wird, darf nicht in den Tunnel oder daraus kommen.
  • Die Tunnel-Konfiguration ergibt sich aus den Permit-Regeln der ACL.

Der Trick besteht also darin, allen Traffic abzulehnen, der nicht in den Tunnel soll, um anschließend alles zuzulassen.

access-list vpn deny ip object-group og-not-local any 
access-list vpn deny ip any object-group og-not-remote
access-list vpn permit ip any any 

Die Objektgruppen, die den Kehrwert der eigentlich zulässigen Netze bilden, sind am einfachsten aus zwei Bereichen zu bauen, die bis an das gewünschte Netz ran gehen.

object-group network og-not-local
 network-object object o-not-local-higher
 network-object object o-not-local-lower

object network o-not-local-lower
 range 0.0.0.0 198.51.99.255
object network o-not-local-higher
 range 198.51.101.0 255.255.255.255

In gleicher Weise werden die inversen Bereiche für das Remote-Netz definiert.

Es kann nun noch vorkommen, dass die ASA sich zickig hat beim annehmen des Verbindungsaufbaus. Dann genügt es die Basisadressen der Netze zu erlauben.

access-list vpn line 1 perm ip host 0.0.0.0 host 0.0.0.0

Tut.

Eine Appliance, die in einer fremden Umgebung Daten einsammelt, soll von extern angesprochen werden. Zum einen muss der Hersteller Fernwartung machen und zum anderen gibt es eine Gruppe von Stellen, die auf die gesammelten Daten zugreifen sollen. Völlig unerwartet wird das geschützte Netzwerk dieser Stellen ebenfalls mit öffentlichen IP Adressen betrieben, so dass für beide Zwecke eine Default Route benötigt wird. Das Ganze soll trotzdem nur mit einer ASA funktionieren. Eine Aufsplittung in mehrere Kontexte (ASA-Virtualisierung) scheitert am fehlenden Support für Remote-Access. Es muss also anders gehen.

Aufgabenstellung

Die Appliance arbeitet in einem anderen, größeren Netzwerk und benutzt selbst nur private IP Adressen (hier 192.0.2.x). Um alle Messstellen erreichen zu können, zeigt die Default Route dieses Gerätes in das fremde Netzwerk (hier grau).

Der Remote-Access Teil (hier grün) ist ein einfaches Setup. Um die vorab unbekannten Clients erreichen zu können, muss die Default Route ins Internet zeigen. Dieser Uplink der ASA benutzt öffentliche IPs (PA-space, hier 189.51.100.x). Die Remote-Access Clients bekommen aus einem Adresspool des lokalen Netzes IPs, so dass sie ohne weiteres Routing auf die Appliance zugreifen können. Von Seiten der Appliance aus erscheint der Client im LAN.

Die ursprüngliche Annahme war, dass die abfragenden Stellen, die über ein extra gesichertes Netzwerk zugeführt werden, ebenfalls in koordinierter Weise mit nicht-öffentlichen Adressen arbeiten würden. In diesem Fall hätte eine spezifische Route zu all diesen Netzen genügt. Allerdings haben die Planer dieses Netzes den Koordinationsaufwand gescheut und verlangen stattdessen, öffentliche IP-Adressen aus dem lokalen PA-Space zu benutzen, der auch normal über Internet erreichbar ist.

ASA multiple default routes

Die Gegenstellen hinter dem geschützten Netzwerk (hier rot) sind vorab ebenso wenig bekannt, wie die Fernwartungs-Clients (grün). In dem hier skizzierten Bild, soll eine solche Gegenstelle eine ganz andere öffentliche IP Adresse haben (hier 203.0.113.133). Um solche Adressen zu erreichen, müsste eine Default Route ins rote Netz gesetzt werden.

Angefasst werden darf nur die ASA, alle anderen Geräte unterliegen fremder Verfügungsgewalt.

Remote-Access

Die Konfiguration für das Remote-Access VPN gemäß Lehrbuch:

interface Ethernet0/0
 nameif green
 security-level 0
 ip address 198.51.100.25 255.255.255.192
!
interface Ethernet0/1
 nameif gray
 security-level 100
 ip address 192.0.2.5 255.255.255.0
!
route green 0.0.0.0 0.0.0.0 198.51.100.1 1
!
ip local pool vpnippool 192.0.2.101-192.0.2.102 mask 255.255.255.0
!
crypto map vpn_outside 999 ipsec-isakmp dynamic vpn_dyn
crypto map vpn_outside interface green
...

Erwartungsgemäß tut das seinen Dienst:

  • Der entfernte Supportmitarbeiter kann per Default Route erreicht werden.
  • Der entfernte Supportmitarbeiter bekommt über das VPN eine IP aus dem inside-LAN.
  • Die Appliance redet mit ihm über das lokale Netzwerk. Extra Routen werden nicht benötigt.

Einmal hin ...

Um die Stellen, die mit öffentlichen IP Adressen aus dem geschützten Netz kommen, versorgen zu können, sind einige Überlegungen notwendig.

Zuerst fällt auf, dass der Verbindungsaufbau bei unbekannten Gegenstellen nur von extern kommen kann. Die Clients werden ausschließlich eine öffentliche IP au dem Netz 198.51.100.248/29 zugreifen können. Von dem privaten Netz hinter der ASA wissen sie nichts.

Damit die Appliance nichts von den fremden Adressen merkt, braucht es eine Doppel-NAT:

interface Ethernet0/2
 nameif red
 security-level 0
 ip address 198.51.100.254 255.255.255.248
!
object network o-server
 host 192.0.2.4
!
nat (red,green) source dynamic any interface destination static interface o-server

Eingehende Pakete über das rote Interface von beliebiger Quelle an die Interface-IP werden so genanntet, dass die Quell-IP zur grünen Interface-Adresse und die Zieladresse, die der Appliance wird. Damit sieht die Appliance nur Anfragen aus ihrem LAN.

Um zu testen, dass diese Idee auch tut, wird ein Testaufbau erstellt:

ASA multiple default routes Test

Anstelle des roten Netzwerkes, steht ein Linux-Rechner, den man passend konfiguriert:

# ip addr add 198.51.100.254/29 dev eth_rot
# ip addr show eth_rot
1: eth_rot: <BROADCAST,MULTICAST,UP,10000>
    inet 198.51.100.254/29 scope global eth_rot
       valid_lft forever preferred_lft forever 

Zusätzlich bekommt er die fremde IP Adresse. Damit er diese auch als Quelladresse benutzt, gibt es eine spezielle extra Route:

# ip addr add 203.0.113.133/32 dev lo
# ip route add 198.51.100.249/32 src 203.0.113.133 dev eth_rot

Wenn man testweise versucht eine Verbindung zur Appliance herzustellen, scheint es erst einmal zu tun.

# ssh 198.51.100.249
15:42:02  IP 203.0.113.133.35158 > 198.51.100.249.22: S 3034099253:3034099253(0)
15:42:03  IP 203.0.113.133.35158 > 198.51.100.249.22: S 3034099253:3034099253(0)
15:42:05  IP 203.0.113.133.35158 > 198.51.100.249.22: S 3034099253:3034099253(0)

Wie man sieht, geht die Anfrage mit der richtigen Absende-IP raus.

Allerdings kommt keine Antwort zurück. Aber warum?

... und zurück

Ein Blick auf die ASA zeigt, das die Datenpakete korrekt nattet und sogar Antworten bekommt.

 16: 15:42:02   192.0.2.5.35158 > 192.0.2.4.22: S 3034099253:3034099253(0) win 14600
 17: 15:42:02   192.0.2.4.22 > 192.0.2.5.35158: S 2009198346:2009198346(0) ack 3034099253 win 14480
 18: 15:42:03   192.0.2.5.35158 > 192.0.2.4.22: S 3034099253:3034099253(0) win 14600
 19: 15:42:03   192.0.2.4.22 > 192.0.2.5.35158: S 2009198346:2009198346(0) ack 3034099253 win 14480
 20: 15:42:05   192.0.2.5.35158 > 192.0.2.4.22: S 3034099253:3034099253(0) win 14600
 21: 15:42:05   192.0.2.4.22 > 192.0.2.5.35158: S 2009198346:2009198346(0) ack 3034099253 win 14480

Warum kommen die Datenpakete nicht wieder zurück? Weil die Default Route fehlt!

Glücklicherweise ist die ASA kein Router. Genauer gesagt die Routingentscheidung wird in zwei Schritten vorgenommen:

  • Steht das ausgehende Interface nicht fest, so wird die generische Routingtabelle befragt, um das Interface zu ermitteln.
  • Ist allerdings klar, auf welchem Bein ausgesendet werden muss, so wird nur der Teil der Routingtabelle betrachtet, der diesem Interface zugeordnet ist.

Eine der Konsequenzen aus diesem Ansatz erklärt die seltsame Anforderung, bei jeder statische konfigurierten Route immer das Interface mit angeben zu müssen: Um so eine schräge Logik umsetzen zu können, muss man erst mal die Angaben dafür haben.

In diesem Fall genügt es also eine weitere Default Route einzutragen, die eine höhere Metrik hat. Dies stellt sicher, dass die ASA keine zwei gleichberechtigten Routen erhält, mit denen sie nicht umgehen kann. Die Situation für die ASA bleibt also eindeutig.

(conf)# route red 0.0.0.0 0.0.0.0 198.51.100.254 2

An der Standard-Routingtabelle ändert sich damit ja nichts:

C    198.51.100.248 255.255.255.248 is directly connected, red
C    198.51.100.0 255.255.255.192 is directly connected, green
C    192.0.2.0 255.255.255.0 is directly connected, gray
S*   0.0.0.0 0.0.0.0 [1/0] via 198.51.100.1, green

Ganz anders ist nun aber die Situation für die Pakete dar, die aus dem (reverse)-NAT heraus fallen.

  • Der NAT Eintrag enthält die Quell- und Ziel-Interfaces, sowie die IP-Adressen und Ports.
  • Pakete bestehender Flows werden also nicht normal behandelt, sondern anhand des vorhandenen NAT-Eintrags.
  • Damit umgehen sie sämtliches konfiguriertes Routing, ACLs etc. insbesondere die Standard-Routingtabelle.
  • Da das Zielinterface fest steht, greift nur noch die Routingtabelle dieses Interfaces, also die niedrig priorisierte Default Route.

Und siehe an:

# ssh  198.51.100.249 
16:38:20  IP 203.0.113.133.51310 > 198.51.100.249.22: S 3979383937:3979383937(0)
16:38:20  IP 198.51.100.249.22 > 203.0.113.133.51310: S 3186400021:3186400021(0) ack 3979383938
16:38:20  IP 203.0.113.133.51310 > 198.51.100.249.22: . ack 1 win 115
16:38:20  IP 198.51.100.249.22 > 203.0.113.133.51310: P 1:22(21) ack 1 win 114
16:38:20  IP 203.0.113.133.51310 > 198.51.100.249.22: . ack 22 win 115
16:38:20  IP 203.0.113.133.51310 > 198.51.100.249.22: P 1:21(20) ack 22 win 115
16:38:20  IP 198.51.100.249.22 > 203.0.113.133.51310: . ack 21 win 114
 136: 16:38:20    192.0.2.5.51310 > 192.0.2.4.22: S 1381971035:1381971035(0) win 14600
 137: 16:38:20    arp who-has 192.0.2.5 tell 192.0.2.4 
 138: 16:38:20    arp reply 192.0.2.5 is-at e8:b7:48:fd:89:81 
 139: 16:38:20    192.0.2.4.22 > 192.0.2.5.51310: S 2009198346:2009198346(0) ack 1381971036 win 14480
 140: 16:38:20    192.0.2.5.51310 > 192.0.2.4.22: . ack 2009198347 win 115
 141: 16:38:20    192.0.2.4.22 > 192.0.2.5.51310: P 2009198347:2009198368(21) ack 1381971036 win 114
 142: 16:38:20    192.0.2.5.51310 > 192.0.2.4.22: . ack 2009198368 win 115
 143: 16:38:20    192.0.2.5.51310 > 192.0.2.4.22: P 1381971036:1381971056(20) ack 2009198368 win 115
 144: 16:38:20    192.0.2.4.22 > 192.0.2.5.51310: . ack 1381971056 win 114
 145: 16:38:20    192.0.2.5.51310 > 192.0.2.4.22: P 1381971056:1381971848(792) ack
 146: 16:38:20    192.0.2.4.22 > 192.0.2.5.51310: . ack 1381971848 win 126

Hurra!

Google's Public-DNS using 8.8.8.8 became the standard for resolving DNS problems. Now other companies try to join. Currently 9.9.9.9 is catching the visitors eye. But this idea generates massive trouble:lawfully and technically.

Competition law

First if all, the is a dramatic shortage on easy memorable IP addresses. There are only 220 usable addresses matching x.x.x.x.

Some companies are already using those IPs for DNS services:

  • 8.8.8.8 PTR google-public-dns-a.google.com.
  • 9.9.9.9 PTR dns.quad9.net.
  • 75.75.75.75 PTR cdns01.comcast.net.
  • 79.79.79.79 PTR public-dns-a.as9105.net.
  • 99.99.99.99 PTR dnsr4.sbcglobal.net.
  • 108.108.108.108 PTR 108-108-108-108.pools.spcsdns.net.
  • 114.114.114.114 PTR public1.114dns.com.

Others are apply them to their own name servers:

  • 77.77.77.77 PTR ns3.hiweb.ir.
  • 93.93.93.93 PTR ns.ngenix.net.
  • 199.199.199.199 PTR NS1.Shane.co.
  • 208.208.208.208 PTR ns1648.ztomy.com.

A few have other ideas:

  • 16.16.16.16 PTR ldtools.gre.hp.com.
  • 23.23.23.23 PTR select.zone.
  • 76.76.76.76 PTR lo0-rtc-svw.nco.riseb.net.
  • 145.145.145.145 PTR Multicast-RendezvousPoint.surf.net.
  • 192.192.192.192 PTR medmgmt-192.tajen.edu.tw.

But most of them just hand out the addresses to ordinary customers. 69 do provide an approbriate reverse DNS entry.

Assuming a valuable business in DNS services running on such addresses, the incredible shortage of this ressouce needs to be addressed in order to keep the entry level for innovative competitors low and the doors open.

Of course, the responsible national agency will say: No chance, this need to be regulated or auctionated.

Do it youself

The immediate consequence of the 9.9.9.9 hype is to keep going on. I did assign the IP 10.10.10.10 for DNS anycast and started with the service.

$ dig AAAA lutz.donnerhacke.de @10.10.10.10 +dnssec +multiline
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 727
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 4, AUTHORITY: 3, ADDITIONAL: 1

;; ANSWER SECTION:
lutz.donnerhacke.de.    CNAME   pro.donnerhacke.de.
lutz.donnerhacke.de.    RRSIG   CNAME 5 3 57600 ...
pro.donnerhacke.de.     AAAA    2001:4bd8:1:1:209:6bff:fe49:79ea
pro.donnerhacke.de.     RRSIG   AAAA 5 3 57600 ...

;; AUTHORITY SECTION:
donnerhacke.de.         NS      avalon.iks-jena.de.
donnerhacke.de.         NS      broceliande.iks-jena.de.
donnerhacke.de.         RRSIG   NS 5 2 57600 ...

;; Query time: 11 msec
;; SERVER: 10.10.10.10#53(10.10.10.10)
;; WHEN: Fri Nov 17 21:04:34 CET 2017
;; MSG SIZE  rcvd: 672

The answer contains an AD flag, which indicates a DNSSEC validated result. This way Security is completed.

Because 10.0.0.0/8 is private IP space, which can not be publically accessed, the customer can't reach out for an name server far far away. On contrary the question will always be answered locally, at the local IP level. So the customer can't be kidnapped by Internet routing tricks. He does act within contractual limits, and his ISP can be held liable for problems.

Using a local server allows the server operator to skip handing out customer information. A secondary effect is, that every cached answer matches every request for any customer. Together with low round trip times DNS responses come back faster.

Local server tend to point to topological near CDN instances, which cause a much smoother Surf feeling. Accessing regional servers decrease the load of ISP peerings, which results in smoother internet for other customers, too.

The remaining problem is to tell the hotline to hand out 10.10.10.10 instead of 8.8.8.8 as before. But it will take time. In the same way the field engineers need to learn to debug DNS problems, and if they fail, to use 10.10.10.10.

There might be a collision with customers using this private IP space internally. They may use 100.100.100.100 instead, I will grant it.

You are not one of our customers? Yell at your ISP to build the same service! Let's make it Best Current Practice to use those IPs all over the world.

Das FreeBSD Handbuch für Portierungen ist eigentlich ziemlich klar. Allerdings funktionieren die Aktionen nur als root. Entwickeln mit solchen Rechnten mag ich nicht. Im IRC und den Mailinglisten heißt es, das sei halt so. Man könne ja mit pourdriere testen. Allerdings benötigt das ebenfalls root-Rechte.

Folgt man dem Handbuch, so ist das alles sehr einfach: Directory anlegen, Makefile schreiben und pkg-descr ausfüllen.

Der nächste Schritt ist das Herunterladen der Quellen. Schon da hatte ich erste Probleme, denn eigentlich wollte ich die Software nicht nochmal separat veröffentlichen. Aber die Idee des Ports ist halt, dass es um die Einbindung von Fremdsoftware geht. Und die liegt halt an anderer Stelle. Also lege ich eine extra Veröffentlichung an.

Damit der Port weiß, dass er die richtigen Dateien läd, werden Prüfsummen erstellt.

$ make makesum
=> parpd-1.1.tgz doesn't seem to exist in /usr/ports/distfiles/.
=> /usr/ports/distfiles/ is not writable by you; cannot fetch.
*** Error code 1

Logisch. Ich bin ja nicht root auf dem System. Und in das Standardverzeichnis darf ich nicht schreiben. Was nun?

In /usr/ports/Mk/bsd.port.mk steht:

# DISTDIR               - Where to search for and store copies of original sources
#                                 Default: ${PORTSDIR}/distfiles

Das kann ich ja umstellen, vielleicht geht's dann?

$ export DISTDIR=/tmp/myport
$ make makesum
=> parpd-1.1.tgz doesn't seem to exist in /tmp/myport/.
=> Attempting to fetch ftp://ftp.iks-jena.de/pub/mitarb/lutz/parpd/parpd-1.1.tgz
parpd-1.1.tgz                                 100% of   16 kB   10 MBps 00m00s
$ cat distinfo
TIMESTAMP = 1510327772
SHA256 (parpd-1.1.tgz) = 95318905767c1123eab87efa4fa664a57e5ed8f697802c6b7d5d0799ad8ea6e6
SIZE (parpd-1.1.tgz) = 17197

Na also! Geht doch.

Alle weiteren Schritte funktionieren dann als Nutzer ohne weitere Probleme.

Nunja, ein Problem gibt es doch noch. Hat man nämlich DEVELOPER=yes in dem make Optionen gesetzt, so erscheint bei jeden Aufruf von make

/!\ parpd-1.1: Makefile warnings, please consider fixing /!\

Not validating first entry in CATEGORIES due to being outside of PORTSDIR.
Please ensure this is proper when committing.

Ist ja auch klar. Natürlich bin ich als normaler Nutzer nicht im originalen PORTSDIR tätig. Die Meldung kann man getrost ignorieren.

Buggy xDSL is an ongoing problem here. We solved it by reducing the netmasks significantly. During the last months, rigid first-hop security filters were introduced into the DSLAMs and different CPEs, unable to deal with short netmasks, occured. We had to really solve the problem.

Problem

Large networks come with large numbers of visible MAC addresses. Several devices (i.e. DSLAMs) behave strangely in such situations, therefore network operators try hard to prevent leakage of frames into other areas. Blocking traffic from a satellite location to a different satellite location is typically called split-horizon. Clients in different parts of the network are unable to communicate with each other.

local proxy arp1

Large layer 2 networks are hard to maintain. Therefore, several security measurements should be implemented at the satellite locations with few clients. Especially DSLAMs are prone to broken first-hop security implementation, which effectively requires to disable those filters or to drop support of non-standard clients,

Often first hop security filters do sniff the communication for DHCP and adjust the filters accordingly. Clients with static IPs do not need to use DHCP (and often have and static configuration instead), which causes the filters to fail to learn the used IP address. Other clients do negotiate more than one IP address, or have full ranges of statically assigned IP addresses. In all such cases, the DLSAM filters are too simple to cover with the required setup.

A common first hop security filter simply drops all broadcast traffic not intended to the learned IP address. The rationale behind this type of filter is, that clients, which already know the MAC of the destination are allowed to reach this device. The canonical way to learn about the destination MAC is to broadcast an ARP packet for desired IP address. So if the filter blocks the distribution of those ARP broadcasts to different clients, only valid communication can be established.

Combining DHCP sniffing, broadcast filters, and split-horizon creates a typical xDSL network, where each client can only communicate with the central router(s). Advanced clients cannot communicate at all.

Simple solution

If only a single router is attached to such a network, enabling local-proxy-arp on this device solves large parts of problem: For every ARP request from a client, the router responds with its own MAC address. So client-client communication is hair pinned at the router interface.

local proxy arp3

Because a router learns the client MAC from the ARP request for the default gateway, it may not need to ask for the client itself. Several routers refresh expiring ARP entries by sending unicast requests, hence the broadcast filter may not cause notifiable trouble.

If there are more than one router or server in the network, the situation becomes complicated. Local-proxy-arp can't be used anymore, because each of the routers will quickly learn each other's MAC address for the client IPs, causing loops.

On the other hand some services, like DHCP, try to verify, that free IP addresses are unused, by arping for that IP. Any generic local-proxy-arp response would cause trouble to such applications.

Different approach

In order to keep the network running, a new daemon (parpd) provides the necessary ARP replies. Depending on configuration rules, the utility respond to ARP requests with the real MAC address of the device or the MAC address of a router (redirect).

It does learn the real MAC-IP pairs by listening to broadcast ARP queries and gratuitous ARP requests. Of course, it does not learn from ARP probes or replies not originated by the device owning the IP. parpd does refresh it's ARP cache using unicast ARP requests. In order to obtain MAC addresses for a redirect response, the request may be broadcasted.

Responses can be delayed, which effectively ignores the first set of requests over a (short) period. This way, special services can probe for the non-existence of an IP, while obtaining necessary answers for real communication.

The customizable set of rules allows adapting the behaviour to complex scenarios.

Example

How to configure the daemon? This way:

cache
 timeout       302     # seconds
 tablesize     3499     # expecting about 10000 entries
 refresh       3*5     # 3 retries a 5 seconds each
 delay         4*3     # respond at 4th retry in 3 seconds
end

interface em0
 timeout       1.011
 # do not respond for queries to our own infrastructure
 rule          0.0.0.0/0        198.51.100.0/29    ignore
 # delay queries from the DHCP server
 rule          198.51.100.4/32  198.51.100.0/24    delay tell
 # help the routers/servers to reach the clients
 rule          198.51.100.0/29  198.51.100.0/24    tell
 # interclient communication through hairpinning at the default gateway
 rule          198.51.100.0/24  198.51.100.0/24    198.51.100.1
 # help erroneous clients arping for everything
 rule          198.51.100.0/24  0.0.0.0/0          verbose 198.51.100.1
 # multihomed server with weak host model
 rule          192.0.2.0/24     198.51.100.0/24    tell
 # show missing entries
 rule          0.0.0.0/0        0.0.0.0/0          verbose ignore
end

Seit Wochen nervt eine bestimmte Kiste mit Bind, dass die Hints für den Startup von der Realität abweicht. Und erstaunlicherweise hat sie recht.

Das Problem

Oct  2 17:07:27 named[1117]: checkhints: b.root-servers.net/AAAA (2001:500:200::b) extra record in hints
Oct  2 17:07:39 named[1117]: checkhints: b.root-servers.net/AAAA (2001:500:84::b) missing from hints
Oct  2 17:07:39 named[1117]: checkhints: b.root-servers.net/AAAA (2001:500:200::b) extra record in hints
Oct  2 17:08:17 named[1117]: checkhints: b.root-servers.net/AAAA (2001:500:84::b) missing from hints
Oct  2 17:08:17 named[1117]: checkhints: b.root-servers.net/AAAA (2001:500:200::b) extra record in hints

Ich kann den Cache löschen, aber das Problem kommt einige Tage später wieder.

Auch die originale Quelle der Hint-Files bestätigt, dass ich nichts falsch konfiguriert habe.

Der Fehler

Wie immer bei DNS-Problemen schaut man bei DNSviz nach.

b.root-servers.net-2017-10-02-15_17_03-UTC

Alles OK? Nein, da ist doch ein kleines Warndreieck!

2017-10-02-172109_238x422_scrot

Das ist exakt die Beschreibung meines Problems: Es gibt im Internet noch Einträge, die die alte, fehlerhafte IP enthalten.

Die Ankündigung passt auch prima zu dem ersten Auftreten des Problems:

As previously announced, B-Root’s IPv6 addresswas renumbered
to 2001:500:200::b, effective 2017-06-01.
(Or IPv6 address previously was 2001:500:84::b; we will continue to
operate service onthe old address for at least 6 months.)

Problem solved?

Die Lösung

Natürlich ist das Problem nicht gelöst. Es muss jemand benachrichtigt werden, der das Problem fixed. Aber wer?

$ dig ns net +short | while read a; do
   echo -n "$a ";
   dig +nottl +nocl aaaa b.root-servers.net @$a |
     egrep -i '^b.root-servers.net.*AAAA';
 done | sort

a.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
b.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
c.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
d.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
e.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
f.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
g.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
h.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
i.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
j.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
k.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
l.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b
m.gtld-servers.net. b.root-servers.net. AAAA    2001:500:84::b

Also alle. Die gesamte Delegation von NET ist kaputt.

Verantwortlich für .net ist Versign. Und die verweisen für die Delegierung auf Verisign (im Auftrag IANA). (Ja, das hatte ich anfangs falsch.)

Domain Name: ROOT-SERVERS.NET
Registry Domain ID: 2751247_DOMAIN_NET-VRSN
Registrar WHOIS Server: whois.networksolutions.com
Registrar URL: http://www.networksolutions.com
Updated Date: 2017-03-05T17:07:51Z
Creation Date: 1995-07-04T04:00:00Z
Registrar Registration Expiration Date: 2020-07-03T04:00:00Z
Registrar: NETWORK SOLUTIONS, LLC.
Registrar IANA ID: 2
Registrar Abuse Contact Email: abuse@web.com
Registrar Abuse Contact Phone: +1.8003337680
Reseller: 
Domain Status: 
Registry Registrant ID: 
Registrant Name: VERISIGN INC.
Registrant Organization: VERISIGN INC.
Registrant Street: 12061 BLUEMONT WAY
Registrant City: RESTON
Registrant State/Province: VA
Registrant Postal Code: 20190-5684
Registrant Country: US
Registrant Phone: +1.7039481212
Registrant Phone Ext: 
Registrant Fax: +1.7039483670
Registrant Fax Ext: 
Registrant Email: noc@verisign.com
Registry Admin ID: 
Admin Name: IANA Root Management, ICANN
Admin Organization: ICANN
Admin Street: 12025 Waterfront Drive #300
Admin City: Los Angeles
Admin State/Province: CA
Admin Postal Code: 90094
Admin Country: US
Admin Phone: +1.13103015800
Admin Phone Ext: 
Admin Fax: +1.13108238649
Admin Fax Ext: 
Admin Email: kim.davies@icann.org

Also schreibe ich die beide mal an.

Beim Erstellen eines Programms, das Daten aus einem Puffer kratzt, stolperte ich über einen sehr seltsamen Fehler in C. Es kostet mich ernstlich Mühe, das Problem auch nur zu verstehen.

Das Programm

Zuerst habe ich das Problem eingedampft auf ein minimales Beispiel:

#include <stdio.h>

int main(void) {
   int len, step;
   char data[5];

   for(len = 27; sizeof(data) < len; len -= step) {
      step = sizeof(data) + 2; 
      printf("len = %3d, taking %d away.\n", len, step);
   }
}

Was tut das Programm?

  • Es gibt einen Puffer mit einer initialen Länge von 27 Byte.
  • Passt die Struktur nicht mehr in den Restpuffer, brich ab.
  • Solange die gesuchte Struktur aber noch rein passt, verarbeite sie (fehlt im Beispiel).
  • Nach der Verarbeitung verkleinere die Rest-Datenmenge um die verarbeitete Struktur plus ein passendes Alignment (hier fest 2).

Der benutzte Puffer, das Verschieben des Arbeitszeigers und die eigentliche Datenverarbeitung sind in dem Beispiel entfallen.

Überraschung

Und hier die Ausgabe:

len =  27, taking 7 away.
len =  20, taking 7 away.
len =  13, taking 7 away.
len =   6, taking 7 away.
len =  -1, taking 7 away.
len =  -8, taking 7 away.
len = -15, taking 7 away.
len = -22, taking 7 away.
len = -29, taking 7 away.
len = -36, taking 7 away.

Wow! Warum beendet sich das Programm denn nicht?

Die Größe der Struktur ist "5". Das ist ganz bestimmt nicht kleiner als "-1", oder etwa doch?

Ganz offensichtlich ist der Vergleich "5" ist kleiner als  "-1" wahr, aber warum?

Evtl. hab' ich ja irgendwelche Warnungen des Compilers übersehen?

$ cc -O2 -Wall -o t t.c
[ ... nichts ... ]
$ cc --version
FreeBSD clang version 3.9.1 (tags/RELEASE_391/final 289601) (based on LLVM 3.9.1)
Target: x86_64-unknown-freebsd11.0
Thread model: posix
InstalledDir: /usr/bin

Nein, auch nicht.

Erklärung

Wenn der C-Compiler den Wert des "signed int" mit dem Namen "len" in einen "unsigned int" verwandeln würde, dann wäre das Ergebnis erklärt. Der Vergleichwert wäre dann also bei mehr als 4 Mrd., also deutlich größer als die Länge der Struktur.

Aber warum sollt der Compiler eine solche Umwandlung durchführen?

Es bleibt nur ein Blick in die Norm:

  • In 6.5.8 "Relational operators" heißt es: If both of the operands have arithmetic type, the usual arithmetic conversions are performed.
  • In 6.3.1.8 "Usual arithmetic conversions" heißt es: If the operand that has unsigned integer type has  rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with  unsigned integer type.
  • In 6.3.1.1 "Boolean, characters, and integers" heißt es: The rank of any unsigned integer type  shall equal the rank of the corresponding signed integer type.

Für den Vergleich zwischen zwei normalen Integer-Typen – der eine mit, der andere ohne Vorzeichen – gilt also, dass beide Operatoren nach unsigned konvertiert werden: In diesem Fall also von -1 auf 4 Mrd.

Voraussetzung für diese Interpretation ist allerdings, dass der sizeof-Operator einen vorzeichenlosen Integer-Typ liefert. Also wieder die Norm lesen:

  • In 6.5.3.4 "The sizeof operator" heißt es: The result is an integer constant. Huch? Keine Aussage zu Vorzeichen?
  • Etwas später liest man: The value of the result is implementation-defined, and its type (an unsigned integer type) is size_t.

Man müsste also das Spiel gewinnen, wenn man die Variable "len" einen größeren Rank verpasst. Leider klappt das weder mit "long" noch mit "long long".

Erst eine beherzter Cast des sizeof-Operators nach "int" löst das Problem:

#include <stdio.h>

int main(void) {
   int len, step;
   char data[5];
   
   for(len = 27; (int)sizeof(data) < len; len -= step) {
      step = sizeof(data) + 2; 
      printf("len = %3d, taking %d away.\n", len, step);
   }
}

Und das ergibt:

len =  27, taking 7 away.
len =  20, taking 7 away.
len =  13, taking 7 away.
len =   6, taking 7 away.

Ende der Geschichte.

Wer mag, kann mal herausbekommen, ob der Umgang des Compilers im Falle von "long" und "long long" standardkonform ist. Ich habe da meine Zweifel.

Heute Nachmittag zeigte eine Kundenmaschine einen spontanen Anstieg der Auslastung. Auf den ersten Blick sah es so aus, dass einfach viel los war, Es gab so viele Anfragen auf die Webseite, dass die Limits erreicht wurden. Diese Grenzen verhindern, dass die Maschine unbenutzbar langsam wird. Dann meldete sich der Kunde und erklärte die Hintergründe.

Lastanstieg

httpd-anfragen

Ein wunderbarer Anstieg an Anfragen zu einem bestimmten Zeitpunkt. Es klingt dann exponentiell ab.

httpd-prozesse

Und er reißt das Limit der kleinen Maschine.

Mehr Prozesse zu starten, würde nur die Bearbeitung der anderen Prozesse noch weiter verlangsamen. Der dadurch entstehende Rückstau aus Datenbank, Platte etc. verschlimmert das Problem nur.

Wenn die Kiste voll beschäftigt ist, nimmt sie keine weiteren Anfragen an. Also meldet sich der Kunde.

Erklärung

Die Webseite sei jetzt genau ganz wichtig, weil man doch einen Projekttag machen würde. Und dazu habe man an sehr viele Schulen in den letzten Wochen Informationen gegeben. Da die Plätze pro Veranstaltung beschränkt sind, habe man sich eine zentrale Anmeldung eingerichtet.

Die Anmeldewebseite sieht so aus:

2017-09-14-190938_777x202_scrot

Wir hatten also eine Menge Schüler, die in einer Menge Schulen darauf warteten, zu einem festen Zeitpunkt auf eine Webseite zu klicken, deren Inhalt aus einer Datenbank generiert wird und dabei komplett fair ACID-Prinzipen ausführen will.

Kurz: Der Kunde hat einen distributed Denial of Service Angriff bestellt und bekommen.

Ich hatte heute ein längeres Gespräch mit einer Journalistin, die fragen zur DDR-Top-level-domain ".dd" hatte. Das Gespräch war lang. Allerdings stellte sich schnell heraus, dass sie zu jung ist, um sich die Realität von vor 30 Jahren vorstellen zu können.

Probleme beim Telefonat

Zuerst kam es zu einem Verständnisproblem der gesellschaftlichen Situation. Sie konnte sich nicht vorstellen, dass man nicht einfach mal in den USA bei Jon Postel anruft, um eine Länderdomain zu registrieren.

Insbesondere gelang es mir nicht, ihr begreiflich zu machen, welche politische Dimension diese Anfrage hat. Ein Mitarbeiter an einer Universität kann nicht im Namen des gesamten Landes sprechen und handeln.

Wesentlich schwerwiegender waren allerdings die inzwischen eingefahrenen Gedankenwege der "Digital Natives". Sie können es sich schlicht nicht vorstellen, ohne Internet und Smartphone zu agieren.

Eine der Fragen war, ob ich ein spontanes Foto hätte, wie ich in der Uni an einem Rechner (es war damals PC-10) sitze. Ganz abgesehen davon, ob diese Aufnahme auf Papier/Film vorliegen könnte, zeigt es das Selbstverständnis eines "Ich dokumentiere mein Leben mit Selfies"-Typs.

Erklärungen im Nachgang

Kurz danach kam eine Liste von Fragen, die es wert sind, halbwegs ernsthaft beantwortet zu werden. Es sind Fragen, die aus der Sicht meiner Kinder gestellt werden könnten. Deswegen veröffentliche ich hier meine Antworten.

Andererseits kann man schon die Frage stellen, ob nicht jemand, der das beruflich macht, mehr vom Telefonat hätte mitnehmen können. Vielleicht sind die Fragen aber auch nur zur Einholung von Zitaten erneut gestellt und pointiert überhöht. Möglicherweise habe ich auch nicht sauber genug erklärt.

Gab es in der DDR bei diesen „internetähnlichen“ Kommunikation auch Dienste, die mit heutiger „GOOGLE“ Suchmaschine zu vergleichen sind? Wenn ja, erklären Sie mir wie und wer dieses Verfahren genutzt hat.

Ja, es gab (und gibt) an den Bibliotheken der Städte, Universitäten und Firmen die Möglichkeit der Katalogrecherche (https://de.wikipedia.org/wiki/Bibliothekskatalog). War die gewünschte Information nicht lokal in der Bibliothek vorrätig, so bediente man sich der Fernleihe (https://de.wikipedia.org/wiki/Fernleihe).

Benennen Sie die drei wichtigsten Gründe, warum die DDR nicht ins World Wide Web gestartet ist und erklären Sie warum.

Der allerwichtigste Grund warum die DDR nicht ins WWW gestartet ist, ist die historische Kausalität. Die DDR ist untergegangen (1990 siehe https://de.wikipedia.org/wiki/Wende_und_friedliche_Revolution_in_der_DDR), bevor das WWW erfunden wurde (1991 siehe https://de.wikipedia.org/wiki/World_Wide_Web). Lediglich die rechtzeitige Erfindung einer Zeitmaschine (https://de.wikipedia.org/wiki/Zeitmaschine) hätte der DDR das WWW bringen können. Dazu ist es aber bis jetzt nicht gekommen.
Der zweite Grund ist, dass für den Betrieb eines Servers (https://de.wikipedia.org/wiki/Server) am Internet eine Standleitung (https://de.wikipedia.org/wiki/Standleitung) benötigt wird, die zu einem anderen Knoten des Internets reicht. Zu dem damaligen Zeitpunkt waren Leitungen (insbesondere Weitverkehrsleitungen) ein sehr rares Gut. Das dauerhafte Belegen einer Telefonwählleitung wurde (und wird) als Fehler behandelt und die Verbindung amtsseitig getrennt. Eine grenzüberschreitende Leitung ins NSW (https://de.wikipedia.org/wiki/Nichtsozialistisches_Wirtschaftsgebiet) stellte eine unüberwindliche politische Hürde da.
Der dritte Grund ist, dass zu dem Zeitpunkt das Internet (https://de.wikipedia.org/wiki/Internet#Ab_1989_Kommerzialisierung_und_das_WWW) als nicht relevant eingestuft wurde. Stattdessen beschäftigten sich die an Netzwerken interessierten Personen mit OSI (https://de.wikipedia.org/wiki/OSI-Modell), i.d.R. also mit X.25 Netzwerken (https://de.wikipedia.org/wiki/X.25). Wer sich mit Internet beschäftigte, war ein akademischer Außenseiter.
Bonus-Info: Zu den ersten deutschen Webseiten *nach der Wende* gehört das Sekteninformationssystem Religio (http://www.religio.de/) aus Thüringen, dass ab 1992 startete.

Beschreiben Sie mir wie sich das  „Surfen“ in Alltag der DDR gestaltete? Was hat man dafür gebraucht und wie funktionierte es. (Sie haben bereits erwähnt, es gab Bastler, die diese Technik zuhause ausgetestet haben).

Da das "Surfen" in der angefragten Zeit noch gar nicht möglich war (mangels WWW), benutzte man andere Formen der elektronischen Kommunikation. Die einzige nennenswerte Verbreitung hatten die Mailbox-Netze (https://de.wikipedia.org/wiki/Mailbox_(Computer)) die nur kurze Telefonverbindungen benötigten. Der Betreiber einer Mailbox musste den Familien-Telefonanschluss (wenn der überhaupt vorhanden war) zweckentfremden. Es war also während der Kommunikation eines Mailboxnutzers oder der Mailbox selbst nicht möglich, das Telefon zu benutzen. Auch musste die Familie lernen, mit dem kurzen Anklingeln durch andere Rechner klar zu kommen. So durfte man nicht sofort ans Telefon gehen, sondern erst, wenn das Klingeln nicht aufhörte.
Daneben gab es erste Internet-Experimente von Enthusiasten in einzelnen Laboren einzelner Universitäten. Dabei wurde höchstens eine Handvoll Rechner lokal zusammen geschaltet. Diese Netze verließen den Laborraum praktisch nicht.

Gibt es offizielle Zahlen, wie viele Bürger damals das „interne“ Internet genutzt haben?

Mangels Internet gab es gar keine Nutzer.

Welche Reaktionen und Folgen hätte das Internet in der DDR, wenn die Domain offiziell genehmigt worden wäre und die Bürger hätten auch „außerhalb“ kommunizieren können?

Die Existenz der Top-Level-Domain ".dd" (https://de.wikipedia.org/wiki/.dd) ist allein durch den reservierten Eintrag in der internationalen Liste der offiziellen Abkürzungen von Ländernamen (https://de.wikipedia.org/wiki/ISO-3166-1-Kodierliste) gerechtfertigt. Die bei einzelnen Experimenten an einzelnen Universitäten benötigten Namen wurden von den Beteiligten durchaus mit dem kanonischen Namen "kiste.uni-xxx.dd" benutzt. Eine über solche Testaufbauten hinaus gehende Verwendung ist nicht bekannt.
Aus der Geschichte der Mailboxnetze vor allem im Zusammenhang mit den Friedensbibliotheken im Vorfeld der Wende zeigt, dass eine aktive Kommunikation aus kleinen Gruppen große Bewegungen machen kann.
Die Kommunikation der DDR-Bürger mit dem Ausland entsprach durchaus den damals üblichen Standards: Es wurden Briefe geschrieben, telefoniert und sich getroffen. Alles natürlich im Rahmen der geltenden Möglichkeiten.

Ein Kunde hat nach einem Umbau am Außenanschluss massive Probleme mit der Stabilität der IPv6 Versorgung. Nach nächtlicher Inaktivität ist am folgenden Morgen keine IPv6 Kommunikation möglich. Beginnt man auf der ASA zu debuggen, verschwindet das Problem sofort.

Heisenbergsches Nichts

Zunächst stellt sich die Frage, was denn so spannendes in der Nacht passiert sein kann. Eigentlich nichts.

Genau das ist das Problem. Es ist nichts passiert. Genau deswegen geht es nicht mehr. Zur Überprüfung lösche ich den IPv6 Nachbarschafts-Cache und das Problem tritt sofort wieder auf. Der IPv6 Nachbarschaftseintrag für das default Gateway will und will nicht wieder kommen.

Pingt man von der ASA aus ein externes Ziel an, verschwindet das Problem instantan. Auch der IPv6 Nachbarschaftseintrag ist wieder vorhanden.

Das Problem kann also kontrolliert ein- und ausgeschaltet werden. Damit ist der Heisenbug in einen Bohrbug verwandelt und die Suche kann beginnen.

Gestörte Nachbarschaft

Zuerst aktiviere ich das Debugging für IPv6 Nachbarschaftskram.

asa# debug ipv6 nd
asa# debug ipv6 icmp
asa# clear ipv6 neighbors
ICMPv6-ND: DELETE -> INCMP: 2001:db8:700:1d::1
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: INCMP deleted: 2001:db8:700:1d::1
ICMPv6-ND: INCMP -> DELETE: 2001:db8:700:1d::1
ICMPv6-ND: DELETE -> INCMP: 2001:db8:700:1d::1
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: INCMP deleted: 2001:db8:700:1d::1
ICMPv6-ND: INCMP -> DELETE: 2001:db8:700:1d::1
ICMPv6-ND: DELETE -> INCMP: 2001:db8:700:1d::1

Das Spiel wiederholt sich endlos.

Die ASA benötigt den Nachbarschaftseintrag des Default Gatways, der gerade fehlt (DELETE), und versucht dann per Neighbour Solication (NS) wieder die MAC zur IP zu ermitteln (INCoMPlete).

Allerdings bekommt sie keine Antwort zurück.

Also jetzt mal von der ASA aus pingen:

asa# ping 2001:db8::1
ICMPv6: Sending echo request to 2001:db8::1
ICMPv6-ND: INCMP deleted: 2001:db8:700:1d::1
ICMPv6-ND: INCMP -> DELETE: 2001:db8:700:1d::1
ICMPv6-ND: DELETE -> INCMP: 2001:db8:700:1d::1
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
?

Es fehlt nach wie vor die Antwort vom gegenüber liegenden Router. Deswegen schreibt der Ping sein erstes "?" hin.

ICMPv6: Sending echo request to 2001:db8::1
ICMPv6-ND: Sending NS for 2001:db8:700:1d::1 on outside
ICMPv6: Received ICMPv6 packet from 2001:db8:700:1d::1, type 136

Und auf einmal ist die Antwort da!

Sofort macht sich die ASA über die Rückmeldung her:

ICMPv6-ND: Received NA for 2001:db8:700:1d::1 on outside from 2001:db8:700:1d::1
ICMPv6-ND: INCMP -> REACH: 2001:db8:700:1d::1
ICMPv6: Received ICMPv6 packet from 2001:db8::1, type 129
ICMPv6: Received echo reply from 2001:4bd8::1
!

Das Ping Echo ist zurück, es schreibt ein "!" auf die Zeile und alles geht wieder.

Wenn man jetzt mal genau hinschaut, erkennt man deutlich den Unterschied zwischen den unbeantworteten Anfragen und der erfolgreichen Anfrage.

Ganz genau hinschauen! Sieht man's?

Der kleine Unterschied

Genau, da ist kein Unterschied.

Aber es muss etwas geben, was den Effekt auslöst. Also muss man noch genauer hinschauen.

asa(conf)# access-list debug-38594 extended permit icmp6 any6 any6 neighbor-advertisement 
asa(conf)# access-list debug-38594 extended permit icmp6 any6 any6 neighbor-redirect 
asa(conf)# access-list debug-38594 extended permit icmp6 any6 any6 neighbor-solicitation 
asa(conf)# access-list debug-38594 extended permit icmp6 any6 any6 router-advertisement 
asa(conf)# access-list debug-38594 extended permit icmp6 any6 any6 router-renumbering 
asa(conf)# access-list debug-38594 extended permit icmp6 any6 any6 router-solicitation
asa# capture d-38594 access-list debug-38594 packet-length 1500 interface outside circular-buffer

Es werden alle Nachbarschafts- und Router-Nachrichten mitgeschnitten. In voller Länge, damit man auch was sehen kann.

Und nun schauen wir mal nach den fehlenden Details:

asa# show capture d-38594 detail
17: 07:36:17.423180 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

18: 07:36:17.433342 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > fe80::6400:0: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255)

19: 07:36:18.415887 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

20: 07:36:18.423211 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > fe80::6400:0: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255)

21: 07:36:19.415887 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

22: 07:36:19.421441 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > fe80::6400:0: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255)

23: 07:36:20.422921 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

24: 07:36:20.428124 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > fe80::6400:0: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255)

25: 07:36:21.415856 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

26: 07:36:21.419350 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > fe80::6400:0: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255) 

Die ASA (fe80::6400:0) fragt per Link-Local Multicast (ff02::1:ff00:1) nach der MAC zur IP 2001:db8:700:1d::1. Soweit war das im Debug schon zu sehen.

Überraschenderweise antwortet die Zielmaschine (2001:db8:700:1d::1) der ASA (fe80::6400:0) mit den gewünschten Daten.

Das bedeutet, dass trotz Umbaus am Uplink keine Störung dort vorliegt: Die Antworten kommen an!

Und es bedeutet weiter, dass es allein ein Problem der ASA ist, was hier abgeht.

Als nächstes wird ein Ping von der ASA aus probiert:

27: 07:36:22.396021 6412.25e3.e7fd 203a.0762.b442 0x86dd Length: 86
 fe80::6400:0 > fe80::223a:7ff:fe62:b442: icmp6: neighbor sol:
 who has fe80::223a:7ff:fe62:b442(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32,hlim 255)

28: 07:36:22.399408 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 78
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor adv:
 tgt is fe80::223a:7ff:fe62:b442(RS) [class 0xe0] (len 24, hlim 255)

29: 07:36:22.416009 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 2001:db8:700:1d::2 > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

30: 07:36:22.425988 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > 2001:1438:700:1d::2: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255)

Zuerst kommt ein Abgleich der Nachbarschaftsbeziehung zwischen den Link-Local-Adressen selbst. Dieser funktioniert.

Und spontan ist die folgende Antwort für die ASA akzeptabel.

Das Problem besteht darin, dass die ASA für durchgereichten Datenverkehr die ND nach dem Default-GW mit der Quell-IP fe80::xxx macht, und dann die Antwort ignoriert. Wenn Sie dagegen selbst pingt, ist die Quell-IP des ND allerdings 2001:... und dann akzeptiert sie auch die Antwort.

Und wie sieht man das in dem Ausgabewust (der hier gekürzt ist)? Mit sort | uniq -c.

Auf der Zielgeraden

Ich hab' mal mit der link-lokal Adresse als Default-Route gespielt, weil ja offenbar kein Problem besteht, die MAC einer Link-Local Adresse zu ermitteln.

Es tritt das gleiche Problem auf:

5081: 11:22:27.164588 6412.25e3.e7fd 3333.ff62.b442 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff62:b442: icmp6: neighbor sol:
 who has fe80::223a:7ff:fe62:b442(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32,hlim 255)

5082: 11:22:27.167609 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor adv:
 tgt is fe80::223a:7ff:fe62:b442(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len32, hlim 255)

5083: 11:22:28.164557 6412.25e3.e7fd 3333.ff62.b442 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff62:b442: icmp6: neighbor sol:
 who has fe80::223a:7ff:fe62:b442(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32,hlim 255)

5084: 11:22:28.165869 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor adv:
 tgt is fe80::223a:7ff:fe62:b442(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len32, hlim 255)

5085: 11:22:29.173925 6412.25e3.e7fd 3333.ff62.b442 0x86dd Length: 86
 fe80::6400:0 > ff02::1:ff62:b442: icmp6: neighbor sol:
 who has fe80::223a:7ff:fe62:b442(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32,hlim 255)

5086: 11:22:29.180914 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor adv:
 tgt is fe80::223a:7ff:fe62:b442(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len32, hlim 255)

Wie bisher: Anfrage von Link-Local wird beantwortet, aber seitens der ASA nicht verarbeitet.

5087: 11:22:30.164664 6412.25e3.e7fd 3333.ff62.b442 0x86dd Length: 86
 2001:db8:700:1d::2 > ff02::1:ff62:b442: icmp6: neighbor sol:
 who has fe80::223a:7ff:fe62:b442(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32,hlim 255)

5088: 11:22:30.172262 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > 2001:db8:700:1d::2: icmp6: neighbor adv:
 tgt is fe80::223a:7ff:fe62:b442(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0](len 32, hlim 255)

Wenn die ASA aber mit ihrer offiziellen Adresse anfragt, gibt es kein Problem.

Und noch etwas fällt auf:

5089: 11:22:31.194234 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor sol:
 who has fe80::6400:0(src lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32, hlim 255)

5090: 11:22:32.200871 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor sol:
 who has fe80::6400:0(src lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32, hlim 255)

5091: 11:22:33.207508 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > fe80::6400:0: icmp6: neighbor sol:
 who has fe80::6400:0(src lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32, hlim 255)

Umgekehrt mag sie auch nicht auf Anfragen nach ihrer eigenen Link-Local Adresse antworten. Das gilt inbesondere auch dann, wenn das Paket schon an sie selbst adressiert ist.

5092: 11:22:35.178915 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 fe80::223a:7ff:fe62:b442 > 2001:db8:700:1d::2: icmp6: neighbor sol:
 who has 2001:db8:700:1d::2(src lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255)

5093: 11:22:35.179464 6412.25e3.e7fd 203a.0762.b442 0x86dd Length: 78
 2001:db8:700:1d::2 > fe80::223a:7ff:fe62:b442: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::2(RS) [class 0xe0] (len 24, hlim 255)

Wohl aber antwortet sie auf die Anfrage an ihre offizielle IP nach ihrer offiziellen IP.

Es scheint so, als ob die ASA die eigene Link-Local Adresse nicht mag.

Der Fehler scheint darin zu bestehen, dass die Link-Local Adresse auf der ASA nicht geändert werden darf. Warum auch immer.

Normalerweise habe ich händisch vergebene Link-Local Adressen nach dem Schema fe80::Gerät:Interface, was die Routingtabellen schön lesbar macht.

Also habe ich die Link-Local Adresse auf "system-default" gestellt und es scheint zu gehen. Selbst nach einem Löschen der Nachbarschafts-Caches erholt sich das System binnen Sekundenfrist von allein.

5123: 14:20:32.560914 6412.25e3.e7fd 3333.ff00.0001 0x86dd Length: 86
 fe80::6612:25ff:fee3:e7fd > ff02::1:ff00:1: icmp6: neighbor sol:
 who has 2001:db8:700:1d::1(src lladdr: 64:12:25:e3:e7:fd) [class 0xe0] (len 32, hlim255)

5124: 14:20:32.569077 203a.0762.b442 6412.25e3.e7fd 0x86dd Length: 86
 2001:db8:700:1d::1 > fe80::6612:25ff:fee3:e7fd: icmp6: neighbor adv:
 tgt is 2001:db8:700:1d::1(RSO)(tgt lladdr: 20:3a:07:62:b4:42) [class 0xe0] (len 32,hlim 255) 

Ein schöner Bug, Cisco.