DHCP: Subnet/Netzmaske/Gateway immer aus System-Netzwerkkonfiguration ableiten

- Neue Helfer _list_network_interfaces() (echte Interfaces aus
  /sys/class/net) und _detect_interface_network() (IPv4/Prefix per
  "ip addr show", Gateway per "ip route show default") — Subnet/
  Netzmaske/Router werden dadurch bei jeder Anzeige/Generierung live vom
  System gelesen statt manuell gepflegt zu werden.
- dhcp_subnet/dhcp_netmask als manuelle Settings entfernt; Interface ist
  jetzt ein Dropdown mit den tatsächlich vorhandenen Interfaces statt
  Freitext, serverseitig zusätzlich gegen die echte Liste validiert.
- _dhcp_reservation_candidates()/_render_kea_config() nehmen jetzt das
  erkannte Netz (ipaddress.IPv4Network) bzw. net_info entgegen statt
  Subnet/Netzmaske aus der Konfiguration zu lesen; Router kommt vom
  erkannten Gateway (Fallback: erster DNS-Eintrag, wie zuvor).
- UI zeigt das erkannte Netz (IP/Prefix, Subnet, Gateway) read-only an;
  Schreiben/Vorschau werden blockiert bzw. liefern eine leere
  Reservierungsliste, wenn die Erkennung fehlschlägt, statt eine mit
  Sicherheit falsche Konfiguration zu erzeugen.
- Live getestet: Erkennung liefert korrekt das tatsächliche WSL-NAT-Netz
  (172.25.64.0/20) samt Gateway; generierte Config besteht kea-dhcp4 -t;
  ein Testgerät mit IP im erkannten Netz erscheint korrekt als
  Reservierung, Geräte außerhalb werden weiterhin sauber übersprungen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 08:28:11 +02:00
co-authored by Claude Sonnet 5
parent c3d69a9870
commit 8e4a874943
3 changed files with 132 additions and 60 deletions
+24 -10
View File
@@ -59,30 +59,44 @@
<div class="section-head" style="margin-bottom:16px;">
<div>
<h2 style="font-size:16px;">Netzwerk-Konfiguration</h2>
<div class="hint">Grundlage für die generierte Subnet- und Reservierungs-Deklaration.</div>
<div class="hint">Subnet, Netzmaske und Gateway werden immer live aus der System-Netzwerkkonfiguration des gewählten Interfaces übernommen — nicht manuell gepflegt, damit sie nie aus dem Ruder laufen können.</div>
</div>
</div>
{% if net_info.ok %}
<div class="flex gap-2" style="align-items:center; margin-bottom:16px; flex-wrap:wrap;">
<span class="pill online">Erkannt</span>
<span class="mono" style="font-size:12.5px;">{{ net_info.ip }}/{{ net_info.prefix }} (Netz {{ net_info.network }})</span>
{% if net_info.gateway %}<span class="text-faint mono" style="font-size:12px;">Gateway {{ net_info.gateway }}</span>{% endif %}
</div>
{% else %}
<div class="flex gap-2" style="align-items:center; margin-bottom:16px;">
<span class="pill offline">Nicht erkannt</span>
<span class="text-faint" style="font-size:12px;">{{ net_info.error }}</span>
</div>
{% endif %}
{% if can_edit %}
<form method="post">
<input type="hidden" name="save_dhcp_config" value="1">
<div class="field"><label>Interface</label>
<input type="text" name="dhcp_interface" value="{{ cfg.dhcp_interface }}">
</div>
<div class="field"><label>Subnet</label>
<input type="text" name="dhcp_subnet" value="{{ cfg.dhcp_subnet }}">
</div>
<div class="field"><label>Netzmaske</label>
<input type="text" name="dhcp_netmask" value="{{ cfg.dhcp_netmask }}">
<select name="dhcp_interface">
{% for iface in interfaces %}
<option value="{{ iface }}" {% if iface == cfg.dhcp_interface %}selected{% endif %}>{{ iface }}</option>
{% endfor %}
</select>
<div class="field-hint">Subnet/Netzmaske/Gateway oben werden für das hier ausgewählte Interface erkannt.</div>
</div>
<div class="field"><label>Range Start</label>
<input type="text" name="dhcp_range_start" value="{{ cfg.dhcp_range_start }}">
</div>
<div class="field"><label>Range Ende</label>
<input type="text" name="dhcp_range_end" value="{{ cfg.dhcp_range_end }}">
<div class="field-hint">Muss innerhalb des oben erkannten Netzes liegen.</div>
</div>
<div class="field"><label>DNS-Server</label>
<input type="text" name="dhcp_dns" value="{{ cfg.dhcp_dns }}">
<div class="field-hint">Kommagetrennt, falls mehrere. Der erste Eintrag wird auch als Router/Gateway verwendet.</div>
<div class="field-hint">Kommagetrennt, falls mehrere. Router/Gateway kommt vom erkannten Netz oben; nur falls dort keins gefunden wird, wird ersatzweise der erste DNS-Eintrag verwendet.</div>
</div>
<div class="field"><label>Domain</label>
<input type="text" name="dhcp_domain" value="{{ cfg.dhcp_domain }}">
@@ -173,7 +187,7 @@
<div class="hint">
{{ reservations|length }} aktive{{ ' Clients' if reservations|length != 1 else 'r Client' }} mit gültiger MAC + IP im konfigurierten Subnet.
{% if skipped_count %}{{ skipped_count }} aktive{{ ' Clients' if skipped_count != 1 else 'r Client' }} ohne MAC/IP übersprungen.{% endif %}
{% if out_of_subnet_count %}{{ out_of_subnet_count }} aktive{{ ' Clients außerhalb' if out_of_subnet_count != 1 else 'r Client außerhalb' }} des konfigurierten Subnets „{{ cfg.dhcp_subnet }}“ übersprungen — Kea lehnt Reservierungen außerhalb ihres Subnets ab.{% endif %}
{% if out_of_subnet_count %}{{ out_of_subnet_count }} aktive{{ ' Clients außerhalb' if out_of_subnet_count != 1 else 'r Client außerhalb' }} des erkannten Netzes{% if net_info.network %} „{{ net_info.network }}“{% endif %} übersprungen — Kea lehnt Reservierungen außerhalb ihres Subnets ab.{% endif %}
</div>
</div>
{% if can_edit %}