OpenBSD: Netzwerk und Routing

OpenBSD: Netzwerk und Routing

OpenBSD-Routing mit ospfd, DHCP-Server dhcpd, IPv6-Konfiguration und Netzwerk-Troubleshooting. Praxisnahe Anleitung für stabile und sichere Netze.

Routing ist das Rückgrat jeder Netzwerkkommunikation. OpenBSD bietet dafür ein transparentes, auditierbares Framework mit klarer Trennung zwischen Kernel- und User-Space-Logik. Die Kernel-Routing-Tabelle verwaltet Paket-Weiterleitung, ospfd und bgpd implementieren dynamische Routing-Protokolle, dhcpd verteilt IP-Adressen automatisch. IPv6-Unterstützung ist vollständig integriert.

Der Artikel zur Systemkonfiguration zeigt Basis-Netzwerk-Konfiguration über hostname.if-Dateien. Dieser Artikel erklärt Routing-Konzepte, DHCP-Server-Betrieb, IPv6-Konfiguration und Netzwerk-Troubleshooting. Die Netzwerk-Grundlagen-Serie vermittelt übergreifende Konzepte – hier konzentrieren wir uns auf OpenBSD-spezifische Implementierungen.

Routing-Grundlagen

Kernel-Routing-Tabelle

Die Kernel-Routing-Tabelle entscheidet über Paket-Weiterleitung. OpenBSD speichert Routen im Kernel-Memory – persistente Speicherung erfolgt in Konfigurationsdateien.

Aktuelle Routing-Tabelle anzeigen:

$ 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 Spalten bedeuten:

  • Destination – Ziel-Netzwerk oder Host
  • Gateway – nächster Hop; bei direkt verbundenen Hosts die 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 – Path-MTU, - bedeutet Interface-Standard
  • Prio – Routing-Priorität; niedrigere Werte werden bevorzugt
  • Iface – ausgehendes Interface

Die Prioritätswerte folgen einem festen Schema:

  • 1 – reserviert für Kernel (lokale Adressen, lo0)
  • 3 – ARP-gelernte Host-Routen
  • 4 – direkt verbundene Netzwerk-Routen
  • 8 – Default-Route und Kernel-interne Routen

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
S STATIC Manuell eingetragene Route
H HOST Route zu einem einzelnen Host (nicht Netzwerk)
L LLINFO Link-Layer-Adresse (MAC) bekannt
C CLONING Erzeugt bei Bedarf neue Host-Routen
c CLONED Durch eine Cloning-Route erzeugt
n CONNECTED Direkt verbundenes Interface
h CACHED Von einer Gateway-Route referenziert
l LOCAL Lokale Adresse dieses Systems
b BROADCAST Lokale Broadcast-Adresse

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.

Statische Routen

Statische Routen definieren explizite Pfade für bestimmte Netzwerke. Sie werden in /etc/hostname.if-Dateien eingetragen.

Default-Route über Interface-Konfiguration:

# /etc/hostname.vio0
inet 192.168.1.10/24
!route add default 192.168.1.1

Das Ausrufezeichen ! führt Befehle während der Interface-Aktivierung aus. Die Default-Route wird nach der IP-Konfiguration gesetzt.

Zusätzliche Netzwerk-Routen für Multi-Subnet-Umgebungen:

# /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

Private Netzwerke werden über einen spezifischen Gateway geroutet. Die Default-Route behandelt Internetverkehr separat.

Host-spezifische Route für eine einzelne IP:

!route add -host 203.0.113.1 192.168.1.253

Der Netzwerkverkehr zu diesem Host nimmt einen alternativen Weg – nützlich für Policy-Routing oder VPN-Konfigurationen. Der -host-Modifier stellt sicher dass die Adresse als Host-Route interpretiert wird.

ℹ️ Info
IP-Routing folgt dem Longest-Prefix-Match-Prinzip: Die spezifischste Route gewinnt.
Eine /32-Route (einzelner Host) hat Vorrang vor einer /24-Netzwerk-Route, die wiederum vor der Default-Route (/0) greift.

Default-Route via /etc/mygate

Die Datei /etc/mygate definiert die Default-Route systemweit. Sie wird von netstart(8) beim Boot gelesen – nach der Interface-Konfiguration, wenn alle hostname.if-Dateien bereits verarbeitet sind.

Einfachster Fall – IPv4-Gateway:

# /etc/mygate
192.168.1.1

IPv4 und IPv6 lassen sich kombinieren – je eine Adresse pro Zeile:

# /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.

Wie in hostname.if(5) können !command-Direktiven Shell-Befehle ausführen – nützlich für Konfigurationsschritte die erst nach vollständiger Interface-Initialisierung möglich sind:

# /etc/mygate
192.168.1.1
!doas rcctl start unbound
⚠️ Wichtig

/etc/mygate und !route add default in hostname.if-Dateien nicht kombinieren – beide Mechanismen tragen eine Default-Route ein. Das Ergebnis ist eine doppelte Default-Route mit undefiniertem Verhalten.

Enthält eine hostname.if-Datei inet autoconf, ignoriert netstart IPv4-Einträge in /etc/mygate automatisch. Bei inet6 autoconf gilt das entsprechend für IPv6.

Routing zwischen Interfaces aktivieren

OpenBSD blockiert standardmäßig Paket-Weiterleitung zwischen Interfaces. Für Router-Funktionalität muss IP-Forwarding aktiviert werden.

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:

$ 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 gesteuert. Die Einträge in /etc/sysctl.conf werden beim nächsten Boot aktiv – für den laufenden Betrieb ist der sysctl-Befehl nötig.

Policy Routing mit rdomains

Routing-Domains (rdomains) erstellen separate Kernel-Routing-Tabellen. Jede Domain hat eigene Routen und ein eigenes Default-Gateway – Interfaces werden einer Domain fest zugeordnet, Prozesse können gezielt in einer bestimmten Domain gestartet werden.

Der Anwendungsfall ist Multi-WAN: zwei Uplinks, zwei unabhängige Routing-Tables, kein gegenseitiges Beeinflussen der Routen.

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

Domain 0 ist die Standard-Domain für alle Prozesse. Domain 1 existiert parallel mit eigenem Gateway – die beiden Routing-Tables beeinflussen sich nicht.

Prozess explizit in einer Domain starten:

$ doas route -T 1 exec /usr/sbin/unbound

Der Prozess und alle seine Kinder nutzen ausschließlich die Routing-Tabelle 1.
Der Netzwerkverkehr läuft über 10.0.0.1 statt über das Default-Gateway in Domain 0.

Aktive Routing-Tables 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
[...]

Jede Domain zeigt ihre eigene Routing-Tabelle – Default-Routen und direkte Netzwerke sind vollständig getrennt. Domain 1 existiert nur wenn sie explizit konfiguriert wurde — ein route -T 1 auf einem System ohne rdomain 1 liefert eine leere Tabelle.

ℹ️ Info
pf(4) kann mit dem rtable-Keyword gezielt eine andere Routing-Tabelle für den Route-Lookup eines Pakets wählen. Das funktioniert ausschließlich auf eingehenden Paketen (in). Die Details zur pf-Integration sind im Artikel Firewall und pf-Grundlagen beschrieben.

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, Pflicht für einfache Konfigurationen
  • interface — Interfaces auf denen OSPF aktiv ist

Berechtigungen setzen — ospfd verweigert den Start bei falschen Rechten:

doas chmod 640 /etc/ospfd.conf
doas chown root:_ospfd /etc/ospfd.conf

Daemon aktivieren und starten:

doas rcctl enable ospfd
doas rcctl start ospfd

Nachbarn prüfen — FULL bedeutet Adjacency vollständig aufgebaut:

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

Routing Information Base zeigt die gelernten 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

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

ℹ️ Info
BGP-Konfiguration unter OpenBSD mit 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 spielt seine Stärken erst beim Routing zwischen autonomen Systemen aus.

Routing-Metriken und Gewichtung

Mehrere Routen zum gleichen Ziel können unterschiedliche Prioritäten haben. OpenBSD verwendet Metriken zur Route-Auswahl.

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

Niedrigere Priorität wird bevorzugt. Route über .254 ist primär, .253 fungiert als Backup.

Dynamische Routing-Protokolle setzen automatisch Prioritäten:

  • OSPF: Basierend auf Interface-Kosten (metric in ospfd.conf)
  • BGP: AS-Path-Länge und lokale Präferenz (bgpd.conf)

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
  • 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

Das Flag vio0 definiert das Interface für DHCP-Anfragen. Der Server antwortet nur auf diesem Interface.

⚠️ Wichtig

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) echte Hochverfügbarkeit: Mehrere Instanzen lassen sich über die Optionen -Y/-y und einen gemeinsamen Schlüssel in /var/db/dhcpd.key in Echtzeit synchronisieren und teilen sich dann dieselbe Lease-Datenbank. Das ist kein Workaround, sondern ein dokumentiertes Feature – Details dazu im Abschnitt SYNCHRONISATION der Manpage.

Statische IP-Zuweisungen

Server und Netzwerk-Hardware benötigen feste IP-Adressen. DHCP kann MAC-Adressen spezifische IPs zuweisen – die host-Deklaration gehört innerhalb den subnet-Block, für den sie gelten soll:

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;
	}
}

Die MAC-Adresse identifiziert das Gerät eindeutig. 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:

doas dhcpleasectl 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
ℹ️ Info
Feste Zuweisungen per host-Block erscheinen nicht in /var/db/dhcpd.leases. Anders als Adressen aus dem range-Pool gelten sie als statische BOOTP-artige Zuweisung, kein dynamisch verbrauchter Lease – die Lease-Datei protokolliert nur Vergaben aus dem Pool.

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

Der ARP-Cache zeigt MAC-Adressen bekannter Hosts. Alternativ in Gerät-Einstellungen nachsehen.

Mehrere Subnetze

Ein DHCP-Server kann mehrere IP-Subnetze über dasselbe Interface bedienen, wenn dieses Interface Adressen aus beiden Netzen trägt (IP-Alias) und die Subnetze im Server per shared-network gruppiert werden.

Alias-Adresse für das zweite Subnetz auf dem Server-Interface anlegen:

doas ifconfig vio1 alias 10.0.1.1/24

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";
}

Ein Client landet nicht zwangsläufig im ersten Subnetz des Pools, selbst wenn dort noch Adressen frei wären. dhcpd streut die Vergabe über alle Subnetze im shared-network-Verbund.

ℹ️ Info
Alternativ lassen sich Subnetze auch über getrennte physische Interfaces bedienen, ohne 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-Netzwerkverkehr ist nicht routebar – Broadcasts erreichen keine anderen Subnetze. Ein DHCP-Relay-Agent lauscht im entfernten Subnetz und leitet 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.

⚠️ Wichtig

Der DHCP-Server benötigt eine subnet-Deklaration für jedes Netz, aus dem er relayte Anfragen erhält – auch wenn dort keine eigene dynamische Vergabe stattfindet. Fehlt sie, verwirft dhcpd die Anfrage kommentarlos: keine Fehlermeldung, kein Log-Eintrag, der Client bleibt im Init-Zustand hängen.

Eine leere Deklaration reicht aus, um das Netz bekannt zu machen:

subnet 172.16.2.0 netmask 255.255.255.0 {
}

Innerhalb desselben shared-network-Blocks wie die Pool-Subnetze platziert, ordnet der Server relayte Clients dann automatisch dem gemeinsamen Adress-Pool zu.

Funktionsnachweis auf Paket-Ebene: tcpdump auf dem DHCP-Server-Interface zeigt die relayte Anfrage mit der Relay-Agent-Adresse im G:-Feld (Gateway/giaddr):

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]

Der Client im entfernten Segment erhält trotz physischer Trennung eine Adresse aus dem Pool des Server-Segments – bestätigt über dhcpleasectl auf dem Client selbst.

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.
⚠️ Wichtig

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:

doas dhcpleasectl 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

Die Ausgabe zeigt zugewiesene Adresse, 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";
}

Die Datei zeigt vergebene IPs, MAC-Adressen und Lease-Zeiten. Jede Lease-Erneuerung erzeugt einen neuen Eintrag – ältere Einträge für dieselbe Adresse bleiben zur Nachvollziehbarkeit in der Datei stehen, statt überschrieben zu werden.

Lease-Datei leeren (alle Clients erhalten neue IPs):

doas rcctl stop dhcpd
doas rm /var/db/dhcpd.leases
doas touch /var/db/dhcpd.leases
doas rcctl start dhcpd

DHCP-Clients erneuern Leases automatisch vor Ablauf. Manuelle Lease-Löschung erzwingt DHCP-Neu-Verhandlung.

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
⚠️ Wichtig
Eine bare inet6-Zeile ohne Adresse und ohne autoconf entspricht keinem der in hostname.if(5) definierten Formate und wird beim Einlesen stillschweigend ignoriert – auch die automatische Link-Local-Adresse (fe80::/10) bleibt dann aus. Für automatische Konfiguration ist inet6 autoconf zwingend, für eine feste Adresse inet6 <adresse> <prefixlen>.

Mit inet6 autoconf generiert der Kernel die Link-Local-Adresse automatisch und aktiviert slaacd(8) für das Interface – ein separater Daemon von dhcpleased, zuständig ausschließlich für IPv6-Autoconfiguration. Router Advertisement (RA) Messages vom lokalen Router konfigurieren zusätzlich Global-Unicast-Adressen.

Aktuelle IPv6-Adressen anzeigen:

$ 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 fe80-Adresse ist Link-Local, nur im lokalen Segment gültig. Die beiden 2001:db8-Adressen sind Global Unicast – eine stabile, aus der Interface-ID abgeleitete Adresse, und eine temporäre Privacy-Extension-Adresse mit zufälligem Suffix (siehe Abschnitt IPv6 Privacy Extensions). pltime/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

Die Neighbor-Solicitation-Nachricht mit Quelladresse :: prüft vor der Vergabe, ob die neue Adresse bereits im Segment verwendet wird (Duplicate Address Detection) – erst danach ist sie 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. Das /64-Präfix ist Standard für Subnetze.

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 beliebig viele 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

Standard-IPv6-Autoconfiguration verwendet die MAC-Adresse für Adress-Generierung. Dies ermöglicht Device-Tracking über Netzwerke hinweg.

Temporäre, zufällige Adressen (RFC 8981) sind unter OpenBSD standardmäßig aktiv, sobald autoconf gesetzt ist – ein separates Einschalten ist nicht nötig. Bereits eine einfache inet6 autoconf-Zeile erzeugt neben der stabilen auch eine temporäre Adresse:

$ ifconfig vio2 inet6
vio2: flags=2a48843<...,AUTOCONF6TEMP,AUTOCONF6,...>
	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

Das AUTOCONF6TEMP-Flag zeigt an, dass zusätzlich zur stabilen autoconf-Adresse eine temporäre generiert wird – erkennbar am Suffix temporary in der Adresszeile. Der Kernel ersetzt diese Adresse automatisch nach 24 Stunden; ausgehende Verbindungen bevorzugen die temporäre Adresse, eingehende laufen weiterhin über die stabile.

ℹ️ Info
Das Schlüsselwort autoconfprivacy aus älteren Anleitungen ist veraltet und funktioniert in Kombination mit autoconf auf derselben Zeile nicht mehr zuverlässig – der aktuelle Name ist temporary, standardmäßig aktiviert und über -temporary abschaltbar.

Privacy Extensions deaktivieren (für Server, die stabile Adressen für eingehende Verbindungen benötigen):

# /etc/hostname.vio2
inet6 autoconf
inet6 -temporary
$ ifconfig vio2 inet6
vio2: flags=2208843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF6,LRO> mtu 1500
	inet6 fe80::5054:ff:fecc:cd08%vio2 prefixlen 64 scopeid 0x3
	inet6 2001:db8:2::30 prefixlen 64

Das AUTOCONF6TEMP-Flag ist verschwunden, es bleibt nur die stabile Adresse. Bereits konfigurierte temporäre Adressen werden dabei nicht sofort entfernt, sondern laufen laut Manpage regulär aus.

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 limitiert auf 4 Pakete. TTL (Time To Live) zeigt verbleibende Hops bis Paket-Verfall. ping beendet sich mit Exit-Status 0, sobald mindestens eine Antwort ankam – bei komplettem Timeout ist der Status größer 0, nützlich für Scripting.

IPv6-Erreichbarkeit:

$ ping6 -c 4 2001:db8:2::30

Das ping6-Kommando funktioniert identisch für IPv6-Adressen.

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

Jede Zeile zeigt einen Hop zum Ziel. Latenz-Werte identifizieren langsame Netzwerk-Segmente. 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 * * *.

ℹ️ Info
Anders als unter Linux/FreeBSD kennt OpenBSDs traceroute(8) keinen -T-Schalter für TCP. Das passende Äquivalent ist -P proto, wobei proto eine Nummer oder ein Name aus /etc/protocols ist. Die Manpage weist ausdrücklich darauf hin, dass das “für die meisten Protokolle nicht zuverlässig funktioniert” – für IP-basierte Fehlersuche bleibt die ICMP-Variante ohne -P meist die robustere Wahl.

Traceroute mit TCP statt UDP:

$ doas traceroute -P tcp example.com
⚠️ Wichtig

Blockt eine aktive pf-Regel (z.B. die Default-Policy block return all) den ausgehenden Netzwerkverkehr von traceroute, meldet der Kernel für lokal generierte Pakete keinen stillen Paketverlust, sondern gibt dem aufrufenden Prozess direkt einen Fehler zurück:

traceroute: sendto: Permission denied

Das betrifft insbesondere TCP- und UDP-basierte Traceroute-Läufe. Zur Diagnose hilft ein Blick ins aktive Regelwerk:

$ doas pfctl -sr | grep -i block
block return all

Bei block return statt block drop liefert pf dem Absender aktiv eine Rückmeldung – daher der EPERM-artige Fehler statt eines Timeouts.

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 Flags zeigen Interface-Status:

  • UP - Interface ist aktiviert
  • RUNNING - Treiber ist funktional
  • MULTICAST - Multicast wird unterstützt
  • AUTOCONF4 - Interface bezieht seine IPv4-Adresse per DHCP (siehe Abschnitt DHCP-Client)
  • LRO - Large Receive Offload, Zusammenfassen mehrerer eingehender Pakete auf Treiber-Ebene

Der status: active bedeutet physische Verbindung besteht. Bei status: no carrier fehlt Netzwerk-Kabel oder Link. 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
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 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. Hohe Fail-Raten deuten auf Hardware-Probleme oder Duplex-Mismatches hin.

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

Die Spalten zeigen Hostname oder IP, MAC-Adresse und Interface. Das Flag l markiert lokale bzw. statisch eingetragene Adressen (permanent). Einträge ohne l sind dynamisch gelernt und laufen nach der Expire-Zeit ab.

Nur numerische Adressen ohne DNS-Auflösung anzeigen:

$ arp -an

Nützlich wenn DNS-Lookups die Ausgabe verlangsamen oder der Resolver nicht erreichbar ist.

ARP-Cache leeren:

$ doas arp -d -a
198.51.100.1 (198.51.100.1) deleted
203.0.113.132 (203.0.113.132) deleted

Nützlich bei MAC-Adress-Änderungen oder Netzwerk-Debugging. Der Cache wird automatisch bei Bedarf neu gefüllt.

Socket-Statistiken

Offene Netzwerk-Verbindungen anzeigen:

$ netstat -an | grep ESTABLISHED
tcp          0     44  192.168.1.65.22        192.168.1.132.52174    ESTABLISHED
ℹ️ Info
OpenBSDs 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 Spalten zeigen:

  • Recv-Q - Empfangene aber nicht gelesene Bytes
  • Send-Q - Gesendete aber nicht bestätigte Bytes
  • Local Address - Lokales IP.Port-Paar
  • Foreign Address - Remote IP.Port-Paar
  • State - TCP-Verbindungsstatus

Listening-Ports identifizieren:

$ 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 die auf allen Interfaces lauschen zeigen *.PORT. Spezifische Adressen (127.0.0.1.PORT, ::1.PORT) limitieren auf einzelne Interfaces – 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 in Echtzeit. Die Ansicht aktualisiert sich sekündlich – nützlich für Verkehrs-Monitoring während Tests.

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 - Keine DNS-Auflösung (schneller)
  • -i vio0 - Spezifisches Interface überwachen

Netzwerkverkehr nach Port filtern:

$ doas tcpdump -n -i vio0 port 22

Zeigt nur Netzwerkverkehr zu und von Port 22. Nützlich für Service-spezifisches Debugging.

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

Zeigt Ping-Anfragen, Router Advertisements und ICMP-Errors.

Paket-Inhalte hexadezimal/ASCII ausgeben:

$ doas tcpdump -n -i vio0 -X icmp

Der -X-Flag zeigt Paket-Payload als Hex- und ASCII-Dump. Nützlich für Protokoll-Debugging – aber unlesbar bei verschlüsseltem Netzwerkverkehr (HTTPS).

ℹ️ Info
Die Reihenfolge der Optionen ist bei 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.

Zeigt A-Record und, falls vorhanden, MX-Records für die Domain. Bei Problemen einen bestimmten 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

Prüft ob Problem bei lokalem DNS-Resolver oder Domain-Konfiguration 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.

Mappt IP zurück auf Hostnamen. Wichtig für E-Mail-Server und einige Service-Authentifikationen.


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: Juli 2026

Michael von den Drachen

entwickelt Bücher, Software und offene Regelwerke rund um technische Systeme, digitale Unabhängigkeit und langlebige Softwarearchitekturen.
Mehr über Michael →
OpenBSD: Netzwerk und Routing
Vorheriger Artikel → OpenBSD: Sicherheitsarchitektur und Schutzkonzepte