OpenBSD trennt die Aufgaben der Netzwerk-Konfiguration klar zwischen Kernel und User Space. Die Kernel-Routing-Tabelle entscheidet über die Paket-Weiterleitung, ospfd und bgpd implementieren dynamische Routing-Protokolle, dhcpd verteilt IP-Adressen automatisch. Die IPv6-Unterstützung ist vollständig integriert.
Der Artikel zur Systemkonfiguration zeigt die Basis-Netzwerk-Konfiguration über hostname.if-Dateien. Die Netzwerk-Grundlagen-Serie vermittelt die übergreifenden Konzepte; hier geht es um deren OpenBSD-spezifische Umsetzung bei Routing, DHCP, IPv6 und Netzwerk-Troubleshooting.
Routing-Grundlagen
Kernel-Routing-Tabelle
Die Kernel-Routing-Tabelle entscheidet über Paket-Weiterleitung. OpenBSD speichert Routen im Kernel-Memory – persistente Speicherung erfolgt in Konfigurationsdateien.
Um die aktuelle Routing-Tabelle anzuzeigen:
$ route -n show
Routing tables
Internet:
Destination Gateway Flags Refs Use Mtu Prio Iface
default 192.168.1.1 UGS 0 0 - 8 vio0
224/4 127.0.0.1 URS 0 0 32768 8 lo0
127/8 127.0.0.1 UGRS 0 0 32768 8 lo0
127.0.0.1 127.0.0.1 UHhl 1 2 32768 1 lo0
192.168.1/24 192.168.1.65 UCn 2 0 - 4 vio0
192.168.1.1 cc:32:e5:e9:1b:0b UHLch 1 2 - 3 vio0
192.168.1.65 52:54:00:ab:cd:09 UHLl 0 3 - 1 vio0
192.168.1.255 192.168.1.65 UHb 1 2 - 1 vio0
[...]
Internet6:
[IPv6-Einträge gekürzt – lo0-interne Routen für Kompatibilitäts-Adressen]
Die Option -n unterdrückt die Namensauflösung und zeigt die Adressen numerisch an.
Die Spalten bedeuten:
- Destination – Ziel-Netzwerk oder Host
- Gateway – die Adresse, an die das Paket als Nächstes geschickt wird. Liegt das Ziel in einem anderen Netz, ist das gewöhnlich die IP-Adresse eines Routers. Bei direkt erreichbaren Zielen kann OpenBSD hier eine Hardware-Adresse des Netzwerkinterfaces anzeigen, beispielsweise eine MAC-Adresse.
- Flags – aktive Route-Eigenschaften (siehe unten)
- Refs – Anzahl aktiver Referenzen auf diese Route
- Use – Anzahl der Pakete, die über diese Route liefen
- Mtu – Maximum Transmission Unit; die maximale Größe eines IP-Pakets in Byte, das über die Route ohne Fragmentierung übertragen werden kann.
-bedeutet, dass für diese Route kein eigener MTU-Wert gesetzt ist. - Prio – Routing-Priorität; niedrigere Werte werden bevorzugt
- Iface – ausgehendes Interface
Die hier sichtbaren Routing-Prioritäten 4 und 8 entstehen beim Anlegen der Routen. Jedes Interface besitzt eine Routing-Priorität zwischen 0 und 15. Standardmäßig beträgt sie 0. Für WLAN-Interfaces verwendet OpenBSD 4, für umb(4) 6 und für carp(4) 15. Die Interface-Priorität kann mit ifconfig geändert werden.
OpenBSD verwendet die Interface-Priorität als Ausgangswert für die Priorität neuer Routen. Eine Route zu einem direkt angeschlossenen Netz entsteht beispielsweise durch die Konfiguration einer IP-Adresse und Netzmaske auf einem Interface. Für eine solche Route addiert OpenBSD 4 (RTP_CONNECTED) zur Interface-Priorität. Für eine neue statische Route werden 8 (RTP_STATIC) addiert. Hat das Interface die Priorität 0, ergeben sich daraus die in der Beispielausgabe sichtbaren Werte 4 und 8.
„Direkt verbunden“ und „statisch“ bezeichnen dabei unterschiedliche Eigenschaften. Direkt verbunden bedeutet, dass das Ziel ohne einen dazwischenliegenden Router über ein Interface erreichbar ist. Statisch bedeutet, dass die Route ausdrücklich eingerichtet wurde und nicht beispielsweise aus der Interface-Konfiguration oder durch ein Routing-Protokoll entstanden ist. Eine statische Route kann deshalb auch zu einem direkt erreichbaren Ziel führen.
Die Flags zeigen den Zustand jeder Route. Die wichtigsten in der täglichen Praxis:
| Flag | Name | Bedeutung |
|---|---|---|
U |
UP | Route ist aktiv |
G |
GATEWAY | Pakete werden über einen Gateway weitergeleitet |
R |
REJECT | Ziel wird als nicht erreichbar zurückgewiesen |
S |
STATIC | Manuell eingetragene Route |
H |
HOST | Route zu einem einzelnen Host (nicht Netzwerk) |
L |
LLINFO | Link-Layer-Adresse (MAC) bekannt |
C |
CLONING | Bei Benutzung dieser Route können daraus neue, spezifischere Routen erzeugt werden |
c |
CLONED | Die Route wurde aus einer Cloning-Route erzeugt |
n |
CONNECTED | Direkt verbundenes Interface |
h |
CACHED | Von einer Gateway-Route referenziert |
l |
LOCAL | Lokale Adresse dieses Systems |
b |
BROADCAST | Route zur Broadcast-Adresse eines lokal angeschlossenen IPv4-Netzes |
Die Default-Route (default) steht für 0.0.0.0/0 – sie greift für alle Pakete ohne spezifischere Route und verweist auf das Gateway im lokalen Netzwerk.
Eine Cloning-Route kann als Ausgangspunkt für weitere, spezifischere Routing-Einträge dienen. Wird eine solche Route benutzt und benötigt der Kernel für ein bestimmtes Ziel einen eigenen Eintrag, kann er diesen aus der vorhandenen Route erzeugen. Die ursprüngliche Route trägt das Flag C, die daraus erzeugte Route das Flag c.
Eine Host-Route ist dagegen lediglich eine Route zu einem einzelnen Zielhost und wird mit H gekennzeichnet. H bedeutet daher nicht automatisch, dass die Route durch Cloning entstanden ist; eine durch Cloning erzeugte Host-Route kann jedoch sowohl c als auch H tragen.
Das Flag b kennzeichnet eine Route zur Broadcast-Adresse eines lokal angeschlossenen IPv4-Netzes. Ein Paket an diese Adresse richtet sich an alle IPv4-Hosts im betreffenden Netzwerksegment. Bei 192.168.1.0/24 ist 192.168.1.255 die zugehörige Broadcast-Adresse. Einen netzübergreifenden „globalen Broadcast“ gibt es nicht; Router leiten solche Broadcasts nicht einfach in andere Netze weiter.
Statische Routen
Statische Routen definieren explizite Pfade für bestimmte Netzwerke. Sie werden in /etc/hostname.if-Dateien eingetragen.
Eine Default-Route über die Interface-Konfiguration einrichten:
# /etc/hostname.vio0
inet 192.168.1.10/24
!route add default 192.168.1.1
Eine Zeile mit ! führt während der Verarbeitung der hostname.if-Datei einen Shell-Befehl aus. Dabei ist die Direktive nicht auf Netzwerkbefehle beschränkt; grundsätzlich kann jeder in dieser Systemumgebung verfügbare Befehl ausgeführt werden. OpenBSD ersetzt außerdem $if innerhalb des Befehls durch den Namen des gerade konfigurierten Interfaces. Für die Netzwerkkonfiguration lässt sich damit beispielsweise route aufrufen, wie hier zum Einrichten der Default-Route.
Zusätzliche Netzwerk-Routen für Multi-Subnet-Umgebungen setzen:
# /etc/hostname.vio0
inet 192.168.1.10/24
!route add default 192.168.1.1
!route add 10.0.0.0/8 192.168.1.254
!route add 172.16.0.0/12 192.168.1.254
Die Route zu 10.0.0.0/8 verwendet das Gateway 192.168.1.254. Für Ziele, zu denen keine spezifischere Route vorhanden ist, verwendet das System die Default-Route.
Eine Host-spezifische Route für eine einzelne IP einrichten:
!route add -host 203.0.113.1 192.168.1.253
-host kennzeichnet 203.0.113.1 als einzelnes Ziel und richtet eine Host-Route über 192.168.1.253 ein.
/32-Route zu einem einzelnen Host ist damit spezifischer als eine passende /24-Netzwerkroute und diese wiederum spezifischer als eine Default-Route (/0).Existieren mehrere Routen mit demselben passenden Präfix, reicht die Präfixlänge allein für die Auswahl nicht aus. Dann werden weitere Eigenschaften der Routen berücksichtigt, beispielsweise ihre Routing-Priorität.
Default-Route via /etc/mygate
Mit /etc/mygate lassen sich beim Systemstart Default-Gateways für IPv4 und IPv6 konfigurieren. netstart(8) verarbeitet die Datei, nachdem die dafür benötigten Interfaces konfiguriert wurden, und richtet die entsprechenden Default-Routen ein.
Der einfachste Fall – IPv4-Gateway:
# /etc/mygate
192.168.1.1
IPv4 und IPv6 lassen sich kombinieren, pro Zeile ein Gateway:
# /etc/mygate
192.168.1.1
2001:db8:1::1
Pro Adressfamilie wird nur die erste Adresse verwendet. Weitere IPv4- oder IPv6-Einträge werden stillschweigend ignoriert.
Auch /etc/mygate kann Zeilen mit !command enthalten. netstart(8) führt den angegebenen Shell-Befehl bei der Verarbeitung der Datei aus:
# /etc/mygate
192.168.1.1
!doas rcctl start unbound
In diesem Beispiel wird der lokale DNS-Resolver unbound mit rcctl gestartet.
inet autoconf ignoriert netstart IPv4-Einträge in /etc/mygate; bei inet6 autoconf gilt das entsprechend für IPv6.
Routing zwischen Interfaces aktivieren
OpenBSD blockiert standardmäßig Paket-Weiterleitung zwischen den Interfaces. Für Router-Funktionalität muss das IP-Forwarding aktiviert werden.
Den aktuellen Status prüfen:
$ sysctl net.inet.ip.forwarding
net.inet.ip.forwarding=0
$ sysctl net.inet6.ip6.forwarding
net.inet6.ip6.forwarding=0
Temporär für das laufende System einschalten:
$ doas sysctl net.inet.ip.forwarding=1
$ doas sysctl net.inet6.ip6.forwarding=1
Dauerhaft via /etc/sysctl.conf:
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
IPv4- und IPv6-Forwarding werden separat aktiviert. Änderungen in /etc/sysctl.conf gelten nach dem nächsten Boot. Sollen sie sofort wirksam werden, müssen die entsprechenden Werte zusätzlich mit sysctl gesetzt werden.
Policy Routing mit rdomains
Routing-Domains (rdomains) trennen Routing-Tabellen voneinander. Interfaces und Prozesse können einer bestimmten rdomain zugeordnet werden.
Ein möglicher Einsatz ist Multi-WAN mit zwei getrennten Uplinks.
Basis-Konfiguration in den Interface-Dateien:
# /etc/hostname.vio0
inet 192.168.1.10/24
rdomain 0
!route -T 0 add default 192.168.1.1
# /etc/hostname.vio1
inet 10.0.0.10/24
rdomain 1
!route -T 1 add default 10.0.0.1
rdomain ordnet das Interface einer Routing-Domain zu. Die Zahl hinter route -T gibt die Routing-Tabelle an, in die der Befehl die Route einträgt. Damit gehört vio0 zu rdomain 0 und erhält seine Default-Route in Routing-Tabelle 0. vio1 gehört zu rdomain 1; seine Default-Route wird in Routing-Tabelle 1 eingetragen.
Einen Prozess explizit in einer Domain starten:
$ doas route -T 1 exec /usr/sbin/unbound
route -T 1 exec startet unbound in Routing-Domain 1. Auch seine Kindprozesse bleiben dieser Routing-Domain zugeordnet. Für Ziele ohne spezifischere Route verwendet der Prozess damit die Default-Route über 10.0.0.1.
Die aktiven Routing-Tabellen anzeigen:
$ route -T 0 -n show -inet
Routing tables
Internet:
Destination Gateway Flags Refs Use Mtu Prio Iface
default 192.168.1.1 UGS 5 133 - 8 vio0
127/8 127.0.0.1 UGRS 0 0 32768 8 lo0
192.168.1/24 192.168.1.65 UCn 2 897 - 4 vio0
192.168.1.1 cc:32:e5:e9:1b:0b UHLch 1 699 - 3 vio0
[...]
-inet beschränkt die Ausgabe auf IPv4.
Routing-Tabelle und Routing-Domain sind nicht dasselbe. Eine Routing-Tabelle enthält die Routen für die Weiterleitung von Paketen. Eine Routing-Domain trennt Netzwerkbereiche voneinander; Interfaces verschiedener rdomains können nicht direkt miteinander kommunizieren und dürfen sogar dieselben IP-Adressen verwenden. Beim Anlegen einer rdomain erzeugt OpenBSD gleichzeitig eine Routing-Tabelle mit derselben Nummer.
Dynamisches Routing mit ospfd
Routing-Protokolle
Statische Routen funktionieren für kleine Netzwerke. Größere Infrastrukturen benötigen dynamisches Routing – Routen werden automatisch basierend auf Netzwerk-Topologie verteilt.
OpenBSD bringt separate Daemons für jedes Routing-Protokoll mit – alle im Basissystem enthalten, kein pkg_add nötig:
- ospfd(8) — Open Shortest Path First, für interne Netzwerke (IGP)
- bgpd(8) — Border Gateway Protocol, für Internet-Routing (EGP)
- ripd(8) — Routing Information Protocol, älteres IGP
- eigrpd(8) — Enhanced Interior Gateway Routing Protocol
OSPF-Konfiguration
OSPF verteilt Routen innerhalb einer Organisation. Alle Router im gleichen OSPF-Bereich (Area) tauschen Routing-Informationen aus.
OSPF-Grundkonfiguration in /etc/ospfd.conf:
router-id 10.0.0.1
area 0.0.0.0 {
interface vio0
interface vio1
}
Die Parameter definieren:
- router-id — Eindeutige Router-Identifikation, typischerweise eine Interface-IP
- area 0.0.0.0 — OSPF-Backbone-Area, in einfachen Single-Area-Setups die übliche Wahl
- interface — Interfaces auf denen OSPF aktiv ist
Daemon aktivieren und starten:
doas rcctl enable ospfd
doas rcctl start ospfd
Nachbarn prüfen:
doas ospfctl show neighbor
ID Pri State DeadTime Address Iface Uptime
10.0.0.2 1 FULL/DR 00:00:39 10.0.0.2 vio1 00:01:00
10.0.0.2 1 FULL/DR 00:00:39 192.168.1.65 vio0 00:01:00
Die Spalten zeigen:
- ID — Router-ID des Nachbarn
- Pri — OSPF-Priorität des Nachbarn bei der Wahl von Designated Router und Backup Designated Router
- State — Zustand der Nachbarschaft;
FULLbedeutet, dass die Link-State-Datenbanken vollständig synchronisiert sind,/DRkennzeichnet den Nachbarn als Designated Router - DeadTime — verbleibende Zeit, bis der Nachbar ohne weitere Hello-Pakete als ausgefallen gilt
- Address — IP-Adresse des Nachbarn auf diesem Netz
- Iface — lokales Interface, über das der Nachbar erreicht wird
- Uptime — Dauer der bestehenden Nachbarschaft
Die OSPF Routing Information Base (RIB) zeigt die vom OSPF-Prozess bekannten Routen:
doas ospfctl show rib
Destination Nexthop Path Type Type Cost Uptime
10.0.0.0/24 10.0.0.1 C Intra-Area Network 10 00:02:13
192.168.1.0/24 192.168.1.62 C Intra-Area Network 10 00:02:13
Dabei bezeichnet Destination das Zielpräfix und Nexthop den nächsten Router beziehungsweise die lokale Adresse zum Ziel. Unter Path Type kennzeichnet C einen direkt verbundenen Pfad; Intra-Area bedeutet, dass der Pfad innerhalb derselben OSPF-Area liegt. Type beschreibt hier mit Network ein Netzwerkziel, Cost die für die Pfadauswahl verwendeten kumulierten OSPF-Kosten und Uptime die Zeit, seit der Eintrag in diesem Zustand besteht.
OSPF aktualisiert die Kernel-Routing-Tabelle automatisch wenn sich die Topologie ändert – fällt ein Router aus, konvergiert das Netzwerk ohne manuellen Eingriff.
Interface-Kosten anpassen:
router-id 10.0.0.1
area 0.0.0.0 {
interface vio0 {
metric 100
}
interface vio1 {
metric 10
}
}
Niedrigere Kosten bevorzugen bestimmte Routen. OSPF berechnet kürzeste Pfade basierend auf kumulativen Interface-Kosten.
BGP-Konfiguration
bgpd(8) ist im Buch „Souveräne Systeme" ausführlich behandelt. BGP-Fehlkonfiguration kann Internet-weite Routing-Probleme verursachen und setzt Kenntnisse in AS-Design und Routing-Policy voraus.
Für kleinere und mittlere Netze genügt OSPF häufig vollständig. BGP ist dagegen für das Routing zwischen autonomen Systemen vorgesehen.
Routing-Metriken und Gewichtung
Mehrere Routen zum gleichen Ziel können unterschiedliche Routing-Prioritäten besitzen. Niedrigere Prioritätswerte werden gegenüber höheren bevorzugt. Diese Kernel-Routing-Priorität ist von den Metriken und Auswahlverfahren dynamischer Routing-Protokolle zu unterscheiden.
Route mit Priorität hinzufügen:
doas route add -priority 10 10.0.0.0/8 192.168.1.254
doas route add -priority 20 10.0.0.0/8 192.168.1.253
Die Route mit Priorität 10 wird gegenüber der Route mit Priorität 20 bevorzugt. Unterschiedliche Prioritäten allein implementieren jedoch keine Prüfung der Gateway-Erreichbarkeit und damit kein automatisches Gateway-Failover. Für mehrere gleichwertige Pfade besitzt OpenBSD zusätzlich Multipath-Routing.
Dynamische Routing-Protokolle besitzen eigene Auswahlkriterien. Bei OSPF beeinflusst metric die OSPF-Pfadberechnung; die Priorität, mit der OSPF-Routen in die Kernel-Tabelle eingetragen werden, wird separat über fib-priority gesteuert. Auch BGP-Attribute wie AS-Path und Local Preference gehören zur BGP-Pfadauswahl und sind nicht mit der Kernel-Routing-Priorität gleichzusetzen.
DHCP-Server mit dhcpd
Grundlegende DHCP-Konfiguration
Der DHCP-Server dhcpd verteilt IP-Adressen automatisch an Clients. Die Konfiguration erfolgt über /etc/dhcpd.conf.
Grundkonfiguration für einzelnes Subnetz:
# /etc/dhcpd.conf
subnet 192.168.1.0 netmask 255.255.255.0 {
range 192.168.1.100 192.168.1.200;
option routers 192.168.1.1;
option domain-name-servers 192.168.1.1;
option domain-name "internal.local";
default-lease-time 3600;
max-lease-time 7200;
}
Die Parameter definieren:
- subnet - Netzwerk-Bereich des DHCP-Servers
- range - IP-Adress-Pool für dynamische Zuweisung
- option routers - Default-Gateway für Clients
- option domain-name-servers - DNS-Server-Liste
- option domain-name - Domainname, den der DHCP-Server an Clients übermittelt
- default-lease-time - Standard-Lease-Dauer in Sekunden
- max-lease-time - Maximale Lease-Verlängerung
DHCP-Server starten:
doas rcctl enable dhcpd
doas rcctl set dhcpd flags vio0
doas rcctl start dhcpd
vio0 beschränkt dhcpd auf dieses Interface; DHCP-Anfragen auf anderen Interfaces werden von dieser Instanz nicht beantwortet.
Zwei unabhängig konfigurierte DHCP-Server im selben Netzwerk-Segment konkurrieren um Lease-Vergaben – bestehende Clients können fehlerhafte oder widersprüchliche Konfigurationen erhalten. Vor dem Testen prüfen, ob im Zielnetz bereits ein DHCP-Server aktiv ist.
Für redundante Konfigurationen unterstützt dhcpd(8) die Synchronisation von Lease-Vergaben zwischen mehreren Instanzen. Mit -Y legt eine Instanz ein Ziel fest, an das sie Synchronisationsmeldungen sendet; mit -y bestimmt sie die Adresse oder das Interface, auf dem sie solche Meldungen empfängt. Ein gemeinsamer Schlüssel in /var/db/dhcpd.key kann die Synchronisationsmeldungen authentifizieren. Die Daemons gleichen ihren Lease-Zustand miteinander ab; sie greifen dabei nicht auf eine gemeinsame Lease-Datei zu. Details beschreibt der Abschnitt SYNCHRONISATION der Manpage.
Statische IP-Zuweisungen
Server und Netzwerk-Hardware benötigen häufig feste IP-Adressen. DHCP kann MAC-Adressen spezifische IPs zuweisen. Eine host-Deklaration muss dabei nicht innerhalb eines subnet-Blocks stehen; die folgende Form gruppiert sie lediglich übersichtlich mit der zugehörigen Netzkonfiguration:
subnet 10.0.0.0 netmask 255.255.255.0 {
range 10.0.0.100 10.0.0.110;
option routers 10.0.0.1;
option domain-name-servers 10.0.0.1;
default-lease-time 600;
max-lease-time 1200;
host client1 {
hardware ethernet 52:54:00:12:34:06;
fixed-address 10.0.0.50;
}
}
Im gezeigten Beispiel wird der Client über seine Ethernet-Adresse zugeordnet. Nach einer Config-Änderung muss dhcpd neu gestartet werden (doas rcctl restart dhcpd) – ein automatisches Neuladen wie bei anderen Daemons per SIGHUP gibt es laut dhcpd(8) nicht.
Client-seitig bestätigt dhcpleasectl, dass die feste Adresse ankommt. Die Option -l vio1 beschränkt die Ausgabe auf die Lease des Interfaces vio1:
doas dhcpleasectl -l vio1
vio1 [Bound]
inet 10.0.0.50 netmask 255.255.255.0
default gateway 10.0.0.1
nameservers 10.0.0.1
lease 10 minutes
dhcp server 10.0.0.2
[Bound] bezeichnet eine erfolgreich gebundene DHCP-Lease. Die folgenden Zeilen nennen Adresse und Netzmaske, Default-Gateway, DNS-Server, Lease-Dauer und den DHCP-Server, der die Adresse vergeben hat.
fixed-address erscheinen nicht als dynamisch vergebene Leases in /var/db/dhcpd.leases. Die Lease-Datei verwaltet die dynamischen Vergaben aus den konfigurierten Pools.
MAC-Adresse eines Geräts ermitteln:
$ arp -a
Host Ethernet Address Netif Expire Flags
printer.example.com 52:54:00:12:34:05 vio0 19m58s
Die Option -a zeigt alle Einträge des ARP-Caches. Die Spalten enthalten Hostname beziehungsweise IP-Adresse, Ethernet-Adresse, zugehöriges Interface, verbleibende Gültigkeitsdauer und gegebenenfalls Flags. Bei Geräten ohne aktuellen ARP-Eintrag muss die MAC-Adresse direkt am Gerät oder in dessen Konfiguration ermittelt werden.
Mehrere Subnetze
Ein shared-network beschreibt mehrere IP-Subnetze, die dasselbe physische Netzwerksegment teilen. Der DHCP-Server behandelt die dynamischen Adressbereiche der enthaltenen Subnetze als gemeinsamen Pool.
Alias-Adresse für das zweite Subnetz auf dem Server-Interface anlegen:
doas ifconfig vio1 alias 10.0.1.1/24
alias fügt dem Interface vio1 die zusätzliche IPv4-Adresse 10.0.1.1/24 hinzu, ohne eine bereits vorhandene Adresse zu ersetzen.
Konfiguration mit zwei Subnetzen im gemeinsamen Pool:
# /etc/dhcpd.conf
shared-network vio1-testnetz {
subnet 10.0.0.0 netmask 255.255.255.0 {
range 10.0.0.100 10.0.0.101;
option routers 10.0.0.1;
option domain-name-servers 10.0.0.1;
default-lease-time 600;
max-lease-time 1200;
}
subnet 10.0.1.0 netmask 255.255.255.0 {
range 10.0.1.100 10.0.1.101;
option routers 10.0.1.1;
option domain-name-servers 10.0.1.1;
default-lease-time 600;
max-lease-time 1200;
}
}
Innerhalb eines shared-network-Blocks bilden alle enthaltenen Subnetze einen gemeinsamen Adress-Pool. Welchem Subnetz ein Client zugeteilt wird, hängt nicht von der Reihenfolge im Pool ab – bei einem Test mit drei Clients auf demselben Segment ergab sich folgende Verteilung:
doas cat /var/db/dhcpd.leases
lease 10.0.0.100 {
client-hostname "hans";
}
lease 10.0.1.100 {
client-hostname "wilhelm";
}
lease 10.0.1.101 {
client-hostname "xaver";
}
client-hostname enthält den vom jeweiligen Client übermittelten Hostnamen.
Ein Client landet nicht zwangsläufig im ersten Subnetz des Pools, selbst wenn dort noch Adressen frei wären. Da die Subnetze laut Konfiguration dasselbe physische Netz bilden, kann der Server anhand des Client-Standorts nicht zwischen ihnen unterscheiden.
shared-network – jedes Interface bekommt dann sein eigenes, unabhängiges subnet, ohne gemeinsamen Pool. Diese Variante ist sinnvoll, wenn die Netze bereits physisch getrennt sind.
DHCP-Relay für entfernte Subnetze
DHCP-Clients beginnen ihre Anfrage per Broadcast im lokalen Netzwerksegment; Router leiten diese Broadcasts nicht in andere Subnetze weiter. Ein DHCP-Relay-Agent lauscht im entfernten Subnetz und leitet die Anfragen gezielt an einen DHCP-Server in einem anderen Netz weiter.
Relay auf dem Router-System aktivieren, der zwischen dem Client-Segment und dem DHCP-Server-Segment sitzt:
doas rcctl enable dhcrelay
doas rcctl set dhcrelay flags "-i vio2 10.0.0.2"
doas rcctl start dhcrelay
Die Parameter definieren:
- -i vio2 - Interface, auf dem der Relay-Agent DHCP-Broadcasts entgegennimmt
- 10.0.0.2 - Ziel-IP des DHCP-Servers, an den Anfragen weitergeleitet werden
Anfragen aus dem vio2-Segment werden an 10.0.0.2 weitergeleitet. Der DHCP-Server antwortet über denselben Relay-Agent zurück zum Client.
Der DHCP-Server benötigt eine subnet-Deklaration für das Netz des relayten Clients. Soll der Client dort dynamisch eine Adresse erhalten, bekommt dieses entfernte Netz einen eigenen passenden Pool:
subnet 172.16.2.0 netmask 255.255.255.0 {
range 172.16.2.100 172.16.2.105;
option routers 172.16.2.1;
option domain-name-servers 172.16.2.1;
default-lease-time 600;
max-lease-time 1200;
}
Das entfernte Netz gehört nicht zusammen mit dem Servernetz in einen shared-network-Block, wenn beide physisch getrennt sind. Der Relay-Agent trägt seine Adresse im giaddr-Feld der DHCP-Anfrage ein; dadurch kann dhcpd die Anfrage dem entfernten Subnetz zuordnen.
Funktionsnachweis auf Paket-Ebene: tcpdump auf dem DHCP-Server-Interface zeigt die relayte Anfrage. -n verhindert die Namensauflösung, -i vio1 wählt das Interface und der Filter port 67 or port 68 lässt Pakete passieren, bei denen Quell- oder Zielport dem DHCP-Server-Port 67 oder dem DHCP-Client-Port 68 entspricht:
doas tcpdump -n -i vio1 port 67 or port 68
172.16.2.1.67 > 10.0.0.2.67: (request) hops:1 xid:0xd76edd47 G:172.16.2.1 ether 52:54:00:cc:cd:01 [|bootp]
In der Ausgabe sendet 172.16.2.1.67 von UDP-Port 67 an den DHCP-Server 10.0.0.2.67. (request) kennzeichnet die BOOTP/DHCP-Anfrage, hops:1 den bereits durchlaufenen Relay-Hop und xid die Transaktions-ID, über die Anfrage und Antwort zusammengehören. G:172.16.2.1 ist die vom Relay eingetragene Gateway-/giaddr-Adresse; ether nennt die Hardware-Adresse des Clients. [|bootp] zeigt an, dass die erfassten Daten für eine vollständige BOOTP-Dekodierung an dieser Stelle nicht ausreichen.
Der Client im entfernten Segment erhält eine Adresse aus dem Pool seines eigenen Subnetzes. Im Test mit Relay-Adresse 172.16.2.1 erhielt der Client 172.16.2.100/24 mit Gateway 172.16.2.1; die Gateway-Adresse war anschließend per ARP und ICMP erreichbar.
DHCP-Client mit dhcpleased
Auf der Client-Seite übernimmt dhcpleased(8) die IP-Konfiguration per DHCP. Ein separates dhclient-Kommando existiert unter OpenBSD nicht mehr.
Daemon aktivieren:
doas rcctl enable dhcpleased
doas rcctl start dhcpleased
Das Interface muss dhcpleased explizit über hostname.if mitgeteilt werden:
# /etc/hostname.vio1
dhcp
Nach dem Anlegen prüft OpenBSD die Datei-Rechte und korrigiert sie bei Bedarf automatisch:
doas sh /etc/netstart vio1
WARNING: /etc/hostname.vio1 is insecure, fixing permissions.
Ein reines ifconfig vio1 down / up liest /etc/hostname.vio1 nicht neu ein. Änderungen an der Datei werden erst über netstart oder einen Reboot wirksam.
War das Interface zuvor statisch konfiguriert, entfernt der Wechsel zu dhcp die alte Adresse nicht automatisch – beide inet-Zeilen bleiben parallel bestehen. Für einen sauberen Wechsel die alte Adresse manuell entfernen (ifconfig vio1 delete <alte-IP>).
Lease-Status direkt beim Client abfragen. Wie oben beschränkt -l vio1 die Ausgabe auf dieses Interface:
doas dhcpleasectl -l vio1
vio1 [Bound]
inet 10.0.0.100 netmask 255.255.255.0
default gateway 10.0.0.1
nameservers 10.0.0.1
lease 10 minutes
dhcp server 10.0.0.2
[Bound] bezeichnet den Zustand einer erfolgreich gebundenen DHCP-Lease. Die folgenden Zeilen zeigen zugewiesene Adresse und Netzmaske, Gateway, DNS-Server, verbleibende Lease-Zeit und den vergebenden DHCP-Server.
Lease-Verwaltung
Aktive DHCP-Leases werden in /var/db/dhcpd.leases gespeichert:
doas cat /var/db/dhcpd.leases
lease 10.0.0.100 {
starts 1 2026/07/27 15:14:17 UTC;
ends 1 2026/07/27 15:24:17 UTC;
hardware ethernet 52:54:00:12:34:06;
uid 01:52:54:00:12:34:06;
client-hostname "client";
}
lease 10.0.0.100 bezeichnet die vergebene IP-Adresse. starts und ends geben Beginn und Ende der Lease in UTC an; die führende Zahl bezeichnet den Wochentag. hardware ethernet enthält die MAC-Adresse des Clients. uid speichert dessen DHCP-Client-Identifier; im gezeigten Fall besteht er aus dem Hardwaretyp 01 für Ethernet und der anschließenden MAC-Adresse. client-hostname enthält den vom Client übermittelten Hostnamen.
Bei Änderungen des Lease-Zustands können für dieselbe Adresse mehrere Einträge in der Datei erscheinen; die Datei sollte deshalb nicht als unveränderliches Verlaufsprotokoll interpretiert werden.
DHCP-Clients erneuern ihre Leases selbstständig vor Ablauf. Das Löschen der serverseitigen Lease-Datei erzwingt bei bereits gebundenen Clients weder eine sofortige Neuverhandlung noch eine neue IP-Adresse. Im Test behielt ein Client nach dem Leeren der Server-Lease-Datenbank 172.16.2.100 und erhielt dieselbe Adresse beim regulären Renewal erneut bestätigt.
IPv6-Konfiguration
IPv6-Adressen und Autoconfiguration
IPv6 nutzt 128-Bit-Adressen statt IPv4s 32 Bit. Die Adress-Struktur unterstützt hierarchisches Routing und Autoconfiguration.
Basis-IPv6-Autoconfiguration in /etc/hostname.if:
# /etc/hostname.vio2
inet6 autoconf
inet6-Zeile konfiguriert weder eine IPv6-Adresse noch SLAAC. hostname.if(5) erlaubt die Adressfamilie auch ohne Adresse; für automatische IPv6-Konfiguration muss jedoch ausdrücklich inet6 autoconf verwendet werden, für eine feste Adresse inet6 <adresse> <prefixlen>.
Mit inet6 autoconf wird das Interface für IPv6-Autoconfiguration markiert (AUTOCONF6). slaacd(8) überwacht entsprechend markierte Interfaces und verarbeitet dort Router Advertisements; daraus können zusätzlich Global-Unicast-Adressen und weitere IPv6-Konfigurationsinformationen entstehen. slaacd ist dabei vom IPv4-DHCP-Daemon dhcpleased getrennt.
Aktuelle IPv6-Adressen anzeigen. Das Argument inet6 beschränkt die Ausgabe auf die IPv6-Konfiguration des Interfaces:
$ ifconfig vio2 inet6
vio2: flags=2a48843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF6TEMP,AUTOCONF6,AUTOCONF4,LRO> mtu 1500
lladdr 52:54:00:cc:cd:01
inet6 fe80::5054:ff:fecc:cd01%vio2 prefixlen 64 scopeid 0x3
inet6 2001:db8:2:0:47f0:731b:6691:7f2e prefixlen 64 autoconf pltime 2651 vltime 5351
inet6 2001:db8:2:0:f185:af7d:7924:cdac prefixlen 64 autoconf temporary pltime 2651 vltime 5351
Die Zahl vor der spitzen Klammer bei flags= ist die numerische Bitmaske der anschließend symbolisch aufgeführten Interface-Flags. Die erste Zeile zeigt dieselben allgemeinen Interface-Eigenschaften wie die spätere vollständige ifconfig-Ausgabe im Abschnitt „Interface-Status prüfen“. AUTOCONF6 kennzeichnet aktivierte automatische IPv6-Konfiguration, AUTOCONF6TEMP die Erzeugung temporärer IPv6-Adressen. mtu ist die Maximum Transmission Unit, lladdr die Link-Layer- beziehungsweise MAC-Adresse.
Die fe80-Adresse ist Link-Local und nur im lokalen Segment gültig; %vio2 ordnet die Link-Local-Adresse dem Interface zu. prefixlen 64 bezeichnet die Präfixlänge, scopeid 0x3 den numerischen Scope-Identifier des Interfaces. Die beiden 2001:db8-Adressen sind Global Unicast – eine persistente automatisch erzeugte Adresse und eine temporäre Privacy-Adresse (siehe Abschnitt IPv6 Privacy Extensions). autoconf kennzeichnet automatisch konfigurierte Adressen, temporary eine temporäre Privacy-Adresse. pltime und vltime geben Preferred- und Valid-Lifetime in Sekunden an.
Der Ablauf lässt sich per tcpdump nachvollziehen – Router Solicitation, Router Advertisement, und Duplicate Address Detection für die neu generierte Adresse:
$ doas tcpdump -n -i vio2 icmp6
fe80::5054:ff:fecc:cd01 > ff02::2: icmp6: router solicitation
fe80::5054:ff:fecc:cd08 > fe80::5054:ff:fecc:cd01: icmp6: router advertisement
:: > ff02::1:ff6a:49e9: icmp6: neighbor sol: who has 2001:db8:2:0:357a:c3ef:b66a:49e9
fe80::5054:ff:fecc:cd01 > ff02::2: icmp6: neighbor adv: tgt is 2001:db8:2:0:357a:c3ef:b66a:49e9
ff02::2 ist die Link-Local-Multicast-Adresse für alle Router. Die Adresse ff02::1:ff6a:49e9 ist die zur geprüften IPv6-Adresse gehörende Solicited-Node-Multicast-Adresse. Die Neighbor-Solicitation-Nachricht (neighbor sol) mit Quelladresse :: fragt mit who has nach der neuen Adresse und prüft vor ihrer Vergabe, ob sie bereits im Segment verwendet wird (Duplicate Address Detection). neighbor adv bezeichnet ein Neighbor Advertisement; tgt is nennt dessen Zieladresse. Erst nach erfolgreicher Prüfung ist die neue Adresse aktiv nutzbar.
Statische IPv6-Konfiguration
Server benötigen feste IPv6-Adressen. Manuelle Konfiguration ähnelt IPv4:
# /etc/hostname.vio2
inet6 2001:db8:2::10/64
CIDR-Notation funktioniert identisch zu IPv4. Für SLAAC werden /64-Präfixe verwendet.
Mehrere IPv6-Adressen über das alias-Keyword:
# /etc/hostname.vio2
inet6 autoconf
inet6 alias 2001:db8:2::30/64
Das alias-Keyword fügt eine zusätzliche, feste Adresse zur automatisch generierten hinzu – nützlich, wenn ein Interface sowohl per SLAAC konfiguriert werden als auch eine zusätzliche, stabile Adresse tragen soll. IPv6 erlaubt mehrere Adressen pro Interface.
Änderung nach dem Bearbeiten der Datei einlesen:
doas sh /etc/netstart vio2
Ergebnis prüfen:
$ ifconfig vio2 inet6
vio2: flags=2248843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF6TEMP,AUTOCONF6,LRO> mtu 1500
lladdr 52:54:00:cc:cd:08
inet6 fe80::5054:ff:fecc:cd08%vio2 prefixlen 64 scopeid 0x3
inet6 2001:db8:2::30 prefixlen 64
Adressen ohne Zeitangaben (pltime/vltime) wie hier sind statisch gesetzt – im Gegensatz zu den befristeten autoconf-Adressen aus dem vorigen Abschnitt laufen sie nicht automatisch ab.
IPv6 Privacy Extensions
OpenBSD verwendet für persistente SLAAC-Adressen standardmäßig Semantically Opaque Interface Identifiers (SOII) nach RFC 7217. Die Interface-ID wird damit nicht einfach aus der MAC-Adresse abgeleitet.
Temporäre, zufällige Adressen (RFC 8981) sind unter OpenBSD standardmäßig aktiviert, sobald autoconf gesetzt ist – ein separates Einschalten ist nicht nötig. Sobald über SLAAC ein geeignetes Präfix verfügbar ist, können neben der persistenten automatisch konfigurierten Adresse auch temporäre Adressen entstehen:
In der oben gezeigten Ausgabe von ifconfig vio2 inet6 stehen dafür zwei automatisch konfigurierte Global-Unicast-Adressen nebeneinander: Die erste trägt autoconf, die zweite zusätzlich temporary. Das bereits dort sichtbare AUTOCONF6TEMP-Flag zeigt an, dass zusätzlich zur persistenten autoconf-Adresse temporäre Adressen erzeugt werden – erkennbar am Suffix temporary in der Adresszeile. Ihre Gültigkeit und Erneuerung wird über die angezeigten Preferred- und Valid-Lifetimes gesteuert.
autoconfprivacy; in den hier behandelten Versionen heißt die Option temporary beziehungsweise -temporary.
Privacy Extensions deaktivieren (für Server, die keine temporären Adressen verwenden sollen):
# /etc/hostname.vio2
inet6 autoconf -temporary
Damit wird die Erzeugung neuer temporärer SLAAC-Adressen deaktiviert. Bereits konfigurierte temporäre Adressen werden dabei nicht sofort entfernt, sondern laufen laut Manpage regulär aus. Die persistente automatisch konfigurierte Adresse bleibt erhalten.
Router Advertisement Daemon
IPv6-Router senden RA-Messages für Autoconfiguration. OpenBSD kann als IPv6-Router fungieren via rad (Router Advertisement Daemon).
Basis-RA-Konfiguration in /etc/rad.conf:
interface vio2 {
prefix 2001:db8:2::/64
}
Die Parameter definieren:
- interface - Ausgehendes Interface für RA-Messages
- prefix - IPv6-Präfix für Clients
Ein dns-Block ist optional und wird nur benötigt, wenn IPv6-DNS-Server per RA verteilt werden sollen.
Router Advertisement Daemon starten:
doas rcctl enable rad
doas rcctl start rad
Konfiguration bei laufendem Daemon neu einlesen, ohne ihn neu zu starten:
doas ractl reload
Clients im Netzwerk konfigurieren sich automatisch mit Adressen aus dem definierten Präfix – sichtbar über slaacd auf Client-Seite, siehe voriger Abschnitt.
Netzwerk-Troubleshooting
Connectivity Tests
Basis-Erreichbarkeit prüfen mit ping:
$ ping -c 4 192.168.1.1
PING 192.168.1.1 (192.168.1.1): 56 data bytes
64 bytes from 192.168.1.1: icmp_seq=0 ttl=64 time=1.108 ms
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.048 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=1.857 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=1.384 ms
--- 192.168.1.1 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.048/1.349/1.857/0.319 ms
Der Parameter -c 4 begrenzt den Test auf vier Echo Requests. Die Kopfzeile nennt das Ziel und mit 56 data bytes die Nutzdatenmenge jedes ICMP-Echo-Requests. Jede Antwortzeile zeigt mit 64 bytes die Größe der empfangenen ICMP-Antwort einschließlich ICMP-Header, mit icmp_seq die laufende Nummer, mit ttl den empfangenen Time-To-Live-Wert und mit time die gemessene Round-Trip-Zeit. Die TTL wird von jedem Router auf dem Weg reduziert; ein Übergang von einem Router zum nächsten wird als Hop bezeichnet. Erreicht die TTL den Wert 0, wird das Paket verworfen.
Die Statistik am Ende zählt gesendete und empfangene Pakete und berechnet daraus den Paketverlust. min/avg/max/std-dev fasst die kleinste, mittlere und größte Round-Trip-Zeit sowie deren Standardabweichung zusammen. ping beendet sich mit Exit-Status 0, sobald mindestens eine Antwort eingegangen ist; bei einem vollständigen Timeout ist der Status größer 0. Dadurch kann ein Skript unterscheiden, ob das Ziel während des Tests erreichbar war.
IPv6-Erreichbarkeit:
$ ping6 -c 4 2001:db8:2::30
ping6 verwendet denselben -c-Parameter und dieselbe grundlegende Ausgabelogik für IPv6-Adressen; statt ICMP für IPv4 wird ICMPv6 verwendet.
Traceroute
Netzwerk-Pfade verfolgen mit traceroute:
$ traceroute 192.168.1.1
traceroute to 192.168.1.1 (192.168.1.1), 64 hops max, 40 byte packets
1 kurt (192.168.1.1) 1.616 ms 1.602 ms 2.454 ms
Die Kopfzeile nennt Ziel, maximale Zahl von 64 Hops und die Größe der verwendeten Probe-Pakete von 40 Byte. Jede folgende Zeile beginnt mit der Hop-Nummer und zeigt den antwortenden Router sowie die Antwortzeiten der drei standardmäßig gesendeten Probes. Jede Zeile zeigt damit einen Hop zum Ziel. Die gemessenen Antwortzeiten liefern Hinweise auf Verzögerungen entlang des Pfads; einzelne hohe Werte beweisen jedoch noch kein langsames Netzwerksegment. In einem flachen Testnetz, in dem alle Systeme an derselben Bridge hängen, ist das Ziel oft nur einen Hop entfernt – in produktiven Netzen mit mehreren Routern dazwischen erscheint entsprechend eine Zeile pro Hop. Bei mehreren möglichen Pfaden (ECMP) kann eine Hop-Zeile auch mehrere unterschiedliche Antwort-Adressen zeigen, eine pro Probe:
3 198.51.100.1 (198.51.100.1) 29.987 ms 31.592 ms 39.918 ms
4 198.51.100.5 (198.51.100.5) 42.059 ms 38.817 ms 198.51.100.9 (198.51.100.9) 40.03 ms
[... weitere Hops ...]
Antwortet ein Hop nicht innerhalb der Timeout-Zeit, erscheint statt Adresse und Latenz ein * * *.
traceroute(8) verwendet standardmäßig UDP-Probes. Mit -I werden stattdessen ICMP- beziehungsweise ICMPv6-Echo-Probes verwendet. -P proto setzt ein anderes IP-Protokoll anhand einer Nummer oder eines Namens aus /etc/protocols; die Manpage weist darauf hin, dass dies für die meisten Protokolle nicht zuverlässig funktioniert.
Traceroute mit ICMP-Echo statt UDP:
$ traceroute -I example.com
Ein anderes IP-Protokoll lässt sich mit -P auswählen, beispielsweise -P tcp. Das ist jedoch nicht mit einem spezialisierten TCP-SYN-Traceroute gleichzusetzen und laut Manpage für die meisten Protokolle nicht zuverlässig.
Bei traceroute: sendto: Permission denied sollte auch das aktive PF-Regelwerk geprüft werden:
traceroute: sendto: Permission denied
$ doas pfctl -sr | grep -i block
block return all
pfctl -sr zeigt die geladenen Filterregeln (-s rules); grep -i block beschränkt die Ausgabe ohne Beachtung von Groß- und Kleinschreibung auf Zeilen mit block. Die Beispielausgabe block return all ist eine Regel, die passenden Verkehr blockiert und eine Rückmeldung erzeugt.
Die genaue Ursache eines lokalen Permission denied darf jedoch nicht allein aus einer vorhandenen block return-Regel abgeleitet werden. Die PF-Semantik und die Rückmeldung an lokal erzeugten Verkehr werden im Artikel Firewall und pf-Grundlagen (geplant) behandelt.
Interface-Status prüfen
Detaillierte Interface-Information:
$ ifconfig vio0
vio0: flags=2808843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF4,LRO> mtu 1500
lladdr 52:54:00:ab:cd:08
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 192.168.1.62 netmask 0xffffff00 broadcast 192.168.1.255
Die erste Zeile nennt die Interface-Flags und die MTU (Maximum Transmission Unit), hier 1500 Byte. Die sichtbaren Flags bedeuten:
- UP - Interface ist aktiviert
- BROADCAST - Interface unterstützt IPv4-Broadcasts
- RUNNING - Treiber beziehungsweise Interface-Ressourcen sind betriebsbereit
- SIMPLEX - Das Interface empfängt seine eigenen gesendeten Frames nicht automatisch als Eingang zurück
- MULTICAST - Multicast wird unterstützt
- AUTOCONF4 - Interface bezieht seine IPv4-Adresse per DHCP (siehe Abschnitt DHCP-Client)
- LRO - Large Receive Offload; der Treiber kann mehrere eingehende TCP-Segmente für die weitere Verarbeitung zusammenfassen
lladdr ist die Link-Layer- beziehungsweise MAC-Adresse. index ist die Kernel-Indexnummer des Interfaces, priority seine Interface-Priorität und llprio die Priorität für Link-Layer-Kommunikation. groups: egress zeigt die Interface-Gruppe, der das Interface angehört. media beschreibt das verwendete Übertragungsmedium und autoselect dessen automatische Auswahl. Die inet-Zeile enthält IPv4-Adresse, Netzmaske und Broadcast-Adresse; 0xffffff00 entspricht der Netzmaske 255.255.255.0 beziehungsweise /24.
Der status: active zeigt einen aktiven Link. status: no carrier weist darauf hin, dass kein Carrier beziehungsweise Link erkannt wird. Bei virtuellen NICs (wie vio unter QEMU/KVM) fehlt oft der sonst übliche Duplex-Zusatz hinter media: (z.B. (1000baseT full-duplex)) – das Fehlen dieser Angabe ist bei virtuellen Interfaces normal und kein Fehlerzeichen.
Paket-Statistiken
Netzwerk-Fehler identifizieren. netstat -i zeigt die Interface-Tabelle mit Adressen und Paket-Zählern:
$ netstat -i
Name Mtu Network Address Ipkts Ifail Opkts Ofail Colls
lo0 32768 <Link> 0 0 0 0 0
lo0 32768 127/8 localhost 0 0 0 0 0
vio0 1500 <Link> 52:54:00:ab:cd:08 2762 0 2453 0 0
vio0 1500 192.168.1/2 hans.fridolin.lan 2762 0 2453 0 0
vio2 1500 <Link> 52:54:00:cc:cd:08 228 0 47 0 0
vio2 1500 172.16.2/24 172.16.2.1 228 0 47 0 0
vio2 1500 fe80::%vio2 fe80::5054:ff:fec 228 0 47 0 0
vio2 1500 2001:db8:2: 2001:db8:2::30 228 0 47 0 0
pflog0 33136 <Link> 0 0 0 0 0
Die Spalten Name, Mtu, Network und Address nennen Interface, MTU, zugehöriges Netzwerk beziehungsweise Adressfamilie und die jeweilige Adresse. Die Zähler bedeuten:
- Ipkts - Empfangene Pakete
- Ifail - Fehlgeschlagene eingehende Pakete
- Opkts - Gesendete Pakete
- Ofail - Fehlgeschlagene ausgehende Pakete
- Colls - Kollisionen (bei Hubs, selten bei Switches)
Jedes Interface erscheint mehrfach: eine Zeile für die Link-Layer-Statistik (<Link>), und je eine weitere Zeile pro konfigurierter Adressfamilie bzw. Netzwerk – ein Interface mit IPv4- und IPv6-Adresse zeigt entsprechend mehrere Zeilen. Steigende Ifail- oder Ofail-Werte sind ein Anlass zur weiteren Diagnose; ihre Ursache lässt sich aus den Zählern allein nicht bestimmen.
ARP-Tabelle
MAC-Adress-Auflösung überprüfen:
$ arp -a
Host Ethernet Address Netif Expire Flags
198.51.100.1 52:54:00:12:34:01 vio1 permanent l
client.example.com 52:54:00:12:34:02 vio0 19m58s
router.example.com 52:54:00:12:34:03 vio0 permanent l
Wie oben zeigt arp -a alle Einträge. Die Spalten zeigen Hostname oder IP, MAC-Adresse, Interface, Ablaufzeit und Flags. Das Flag l kennzeichnet einen lokalen ARP-Eintrag. Ob ein Eintrag abläuft, zeigt die Spalte Expire; mit permanent gekennzeichnete Einträge laufen nicht ab.
Nur numerische Adressen ohne DNS-Auflösung anzeigen. -a wählt alle Cache-Einträge, -n unterdrückt die Namensauflösung:
$ arp -an
Die numerische Ausgabe vermeidet Verzögerungen durch DNS-Lookups und funktioniert auch bei nicht erreichbarem Resolver.
ARP-Cache leeren. -d löscht Einträge; zusammen mit -a wird die Löschoperation auf alle Cache-Einträge angewendet:
$ doas arp -d -a
198.51.100.1 (198.51.100.1) deleted
203.0.113.132 (203.0.113.132) deleted
Das Leeren des Caches erzwingt bei der nächsten Kommunikation eine neue ARP-Auflösung. Das ist beispielsweise nach einer MAC-Adress-Änderung oder bei der Fehlersuche hilfreich. Der Cache wird anschließend automatisch wieder gefüllt.
Socket-Statistiken
Offene Netzwerk-Verbindungen anzeigen. Bei netstat zeigt -a alle Sockets, -n Adressen und Ports numerisch; grep ESTABLISHED lässt aus dieser Ausgabe nur TCP-Verbindungen im Zustand ESTABLISHED stehen:
$ netstat -an | grep ESTABLISHED
tcp 0 44 192.168.1.65.22 192.168.1.132.52174 ESTABLISHED
netstat trennt IP-Adresse und Port mit einem Punkt (192.168.1.65.22), nicht mit einem Doppelpunkt wie bei vielen Linux-Tools üblich.
Die erste Spalte nennt mit tcp beziehungsweise tcp6 das Transportprotokoll und die Adressfamilie. Die weiteren Spalten zeigen:
- Recv-Q - Empfangswarteschlange des Sockets
- Send-Q - Sendewarteschlange des Sockets
- Local Address - Lokales IP.Port-Paar
- Foreign Address - Remote IP.Port-Paar
- State - TCP-Verbindungsstatus
Listening-Ports identifizieren. Zusätzlich zu -a und -n zeigt -l nur lauschende Sockets; grep LISTEN beschränkt die Textausgabe auf den TCP-Zustand LISTEN:
$ netstat -anl | grep LISTEN
tcp 0 0 *.22 *.* LISTEN
tcp 0 0 127.0.0.1.25 *.* LISTEN
tcp6 0 0 fe80::1%lo0.25 *.* LISTEN
tcp6 0 0 ::1.25 *.* LISTEN
tcp6 0 0 *.22 *.* LISTEN
Services mit einer Wildcard-Bindung zeigen *.PORT. *.* in der Spalte Foreign Address bedeutet bei einem lauschenden Socket, dass noch kein bestimmter entfernter Endpunkt gebunden ist. Spezifische lokale Adressen (127.0.0.1.PORT, ::1.PORT) beschränken den Socket auf diese Adresse – IPv4 und IPv6 erscheinen dabei als getrennte Einträge (tcp vs. tcp6).
Für Live-Statistiken mit automatischer Aktualisierung:
$ systat netstat
Das systat-Tool zeigt Netzwerk-Aktivität fortlaufend an. Standardmäßig wird die Ansicht alle fünf Sekunden aktualisiert; mit -s lässt sich das Intervall ändern, um den Verkehr während Tests in kürzeren oder längeren Abständen zu beobachten.
tcpdump für Paket-Analyse
Netzwerkverkehr live mitschneiden:
$ doas tcpdump -n -i vio0
tcpdump: listening on vio0, link-type EN10MB
07:40:01.579084 192.168.1.65.22 > 192.168.1.132.52174: P 2031605786:2031605822(36) ack 150708228 win 271 <nop,nop,timestamp 4109385259 2708599083> (DF) [tos 0xb8]
07:40:01.590399 192.168.1.65.22 > 192.168.1.132.52174: P 36:112(76) ack 1 win 271 <nop,nop,timestamp 4109385269 2708599083> (DF) [tos 0xb8]
[... weitere Pakete ...]
Die Parameter:
- -n - Adressen und Ports numerisch anzeigen, ohne Namensauflösung
- -i vio0 - Pakete auf dem Interface
vio0mitschneiden
Die Meldung listening on vio0, link-type EN10MB bestätigt das überwachte Interface und bezeichnet mit EN10MB den von BPF verwendeten Ethernet-Link-Typ. In jeder Paketzeile steht zuerst der Zeitstempel. Danach folgen Quelladresse und Quellport, > und Zieladresse mit Zielport. Beim gezeigten TCP-Paket bedeutet P ein gesetztes PSH-Flag; 2031605786:2031605822(36) bezeichnet den Sequenznummernbereich und die darin enthaltenen 36 Byte Nutzdaten. ack nennt die bestätigte nächste Sequenznummer und win das angekündigte TCP-Empfangsfenster. In <...> stehen TCP-Optionen, hier zwei No-Operation-Fülloptionen und die TCP-Timestamps. Bei timestamp 4109385259 2708599083 ist der erste Wert der aktuelle TCP Timestamp Value (TSval), der zweite der vom Kommunikationspartner zurückgespiegelte Timestamp Echo Reply Value (TSecr). (DF) zeigt das IPv4-Flag „Don’t Fragment“. [tos 0xb8] gibt das Type-of-Service-Byte des IPv4-Headers hexadezimal an; 0xb8 entspricht hier DSCP 46 (Expedited Forwarding) bei nicht gesetzten ECN-Bits. In der zweiten Zeile stellt 36:112(76) die Sequenznummern relativ zur ersten beobachteten Sequenz dar.
Netzwerkverkehr nach Port filtern:
$ doas tcpdump -n -i vio0 port 22
Für die Fehlersuche an einem einzelnen Dienst lässt sich die Aufzeichnung auf dessen Netzwerkverkehr beschränken. Dieser Filter erfasst nur Pakete zu und von Port 22.
ICMP-Verkehr anzeigen – dafür eignet sich ein paralleler ping von einem anderen Host als Testlast:
$ doas tcpdump -n -i vio0 icmp
tcpdump: listening on vio0, link-type EN10MB
07:43:12.985240 192.168.1.62 > 192.168.1.65: icmp: echo request
07:43:12.985575 192.168.1.65 > 192.168.1.62: icmp: echo reply
Der Zeitstempel steht am Anfang jeder Zeile; Quell- und Zieladresse werden durch > getrennt. icmp: echo request ist die Echo-Anfrage, icmp: echo reply die zugehörige Antwort. Der Filter zeigt außerdem andere IPv4-ICMP-Nachrichten, wenn sie auf dem Interface auftreten. IPv6 verwendet ICMPv6 und wird separat gefiltert:
$ doas tcpdump -n -i vio0 icmp6
Darüber lassen sich beispielsweise Router Solicitations, Router Advertisements und Neighbor Discovery beobachten.
Paket-Inhalte hexadezimal/ASCII ausgeben:
$ doas tcpdump -n -i vio0 -X icmp
Mit dem -X-Flag gibt tcpdump die Paketdaten als Hex- und ASCII-Dump aus. Dadurch wird auch der übertragene Nutzdateninhalt sichtbar. Bei verschlüsselten Protokollen bleibt dieser ohne Entschlüsselung jedoch nicht als Klartext lesbar.
tcpdump wichtig: -i erwartet zwingend ein Argument. -i -X vio0 interpretiert -X fälschlich als Interface-Namen, gefolgt vom Fehler “Failed to open bpf device”. Korrekt ist -i vio0 -X oder -X an beliebiger anderer Position außerhalb direkt hinter -i.
DNS-Auflösung testen
DNS-Lookup durchführen:
$ host example.org
example.org has address 203.0.113.10
example.org mail is handled by 10 mail.example.org.
Die Ausgabe enthält den A-Record und, falls vorhanden, die MX-Records der Domain. Beim MX-Eintrag ist 10 die Präferenz des Mailservers; kleinere Werte werden gegenüber größeren bevorzugt. Bei Problemen lässt sich ein bestimmter DNS-Server gezielt abfragen, statt den konfigurierten System-Resolver zu nutzen:
$ host example.org 192.168.1.1
Using domain server:
Name: 192.168.1.1
Address: 192.168.1.1#53
example.org has address 203.0.113.10
Im Block Using domain server ist Name der abgefragte DNS-Server, Address seine Adresse und #53 der verwendete DNS-Port 53. Der Vergleich hilft einzugrenzen, ob die Ursache beim konfigurierten lokalen Resolver oder an anderer Stelle der DNS-Auflösung liegt. Im gezeigten Testnetz übernimmt der lokale unbound-Resolver diese Aufgabe – öffentliche Drittanbieter-Resolver wie die bekannten 1.1.1.1 oder 9.9.9.9 widersprechen dem Souveränitäts-Gedanken dieser Serie und eignen sich höchstens für einen punktuellen Vergleich.
Reverse-DNS-Lookup:
$ host 203.0.113.10
10.113.0.203.in-addr.arpa domain name pointer example.org.
Für IPv4 bildet host dazu den Namen in der Reverse-DNS-Zone in-addr.arpa: Die Oktette der Adresse erscheinen dort in umgekehrter Reihenfolge, anschließend verweist ein PTR-Record (domain name pointer) auf den Hostnamen. Reverse DNS löst damit eine IP-Adresse zurück zu einem Hostnamen auf. Reverse DNS spielt insbesondere beim Betrieb von Mailservern und bei verschiedenen Zugriffs- und Vertrauensprüfungen eine wichtige Rolle.
Netzwerk als nachvollziehbarer Systemzustand
Mit der Fehlersuche ist der Blick einmal durch die verschiedenen Ebenen des Netzwerks gewandert: vom erreichbaren Ziel über Routing und Interfaces bis zum einzelnen Paket. Damit lässt sich nicht nur feststellen, dass eine Verbindung scheitert, sondern auch, an welcher Stelle der erwartete Netzwerkzustand vom tatsächlich beobachteten Zustand abweicht.
OpenBSD behandelt Netzwerkkonfiguration damit nicht als einen einzelnen, zentralen Zustand. Adressen, Routen, Routing-Domains und dynamisch gelernte Informationen entstehen an unterschiedlichen Stellen, wirken aber gemeinsam darauf, wie der Kernel ein Paket weiterleitet. Gerade diese Trennung macht komplexere Netzwerke nachvollziehbar: Jede Änderung lässt sich ihrem Ursprung zuordnen und ihre Wirkung im resultierenden Netzwerkzustand überprüfen.
Damit ist geklärt, wohin OpenBSD Pakete schickt und wie sich ihr Weg nachvollziehen lässt. Ob sie diesen Weg auch nehmen dürfen, ist die nächste Ebene. Sie führt zum Artikel Firewall und pf-Grundlagen (geplant).
Verwendete Software und Versionen
Diese Anleitung bezieht sich auf:
- OpenBSD: 7.8, 7.9
- ospfd: Integriert in OpenBSD Base
- dhcpd: Integriert in OpenBSD Base
- rad: Integriert in OpenBSD Base
- Kontext: Server und Router-Systeme
- Stand: September 2026