Initial import from bf-network-r6.opencode — opencode migration

This commit is contained in:
2026-08-26 12:44:21 +02:00
commit 5ee0716dda
297 changed files with 72324 additions and 0 deletions

5
switch/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.secrets
cookies.txt
state-*/
firmware/
cli/running-*.txt

4
switch/.secrets.example Normal file
View File

@@ -0,0 +1,4 @@
# copy to .secrets (gitignored). Account = Web/Telnet login on the switch.
export SW_HOST=10.40.41.239
export SW_USER=claudecode
export SW_PASS=changeme

31
switch/README.md Normal file
View File

@@ -0,0 +1,31 @@
# 📦 ARCHIV — ALLNET-Switch (`sw1`) Telnet-Runner
> **Dieses Verzeichnis ist Historie. Das Gerät existiert nicht mehr.**
> Der ALLNET-Core-Switch `sw1` wurde am **2026-08-08** durch `crs326-core` (MikroTik CRS326, RouterOS)
> ersetzt und **physisch ausgebaut**; in Nautobot steht er auf *Decommissioned* (TP22 Schritt 5).
> Der Omada-Switch `sw2` ist bereits am **2026-07-23** durch `crs326-branch` ersetzt worden (TP15).
## Warum es trotzdem liegen bleibt
Nachvollziehbarkeit: die `.cli`-Dateien und Klickanleitungen dokumentieren, **wie** die VLAN-Struktur
(10/20/30/40/50/90/99/100) ursprünglich entstanden ist — TP1 ist ohne sie nicht rekonstruierbar.
Projektkonvention ist *fortschreiben statt löschen*.
## Was hier **nicht** mehr gilt
| Datei / Ordner | Status |
|---|---|
| `cli/*.cli` + `cli/run-cli.exp` | ❌ nicht mehr ausführbar — kein Gerät, kein Telnet-Endpunkt |
| `cli/03-trunk-ap-ports.cli` | ❌ war schon vor dem Ausbau **stale** (vom Edge-Cutover 2026-07-01 überholt) |
| `ui-guides/01-trunk-ap-ports.md` | 📜 historisch (ausgeführt) |
| `ui-guides/02-vlan99-mgmt-cutover.md` | ⚪ **nie ausgeführt und gegenstandslos** — der Mgmt-VLAN-Umzug für sw1 entfiel mit dem Gerät (TP19.1) |
| `state-backups/sw1-running-config-*.txt` | ⚠️ 214-Byte-Fehlschläge (`Incorrect User Name!!`, toter `claudecode`-Login) — kein verwertbarer Config-Stand |
| `state-PRE/`, `firmware/`, `recovery/`, `sg8316m*` | 📜 Archiv |
## Wo die Wahrheit heute steht
- **Ist-Stand + TP-Steuerung:** `docs/teilprojekte.md`
- **Config-Weg:** ausschließlich **Nautobot Golden Config** (TP21/TP22) — keine Direkt-Changes an Geräten
- **Interaktiv/Debug:** MikroMCP (`crs326-core`, `crs326-branch`, `ap1`, `ap2`)
*Archiviert 2026-08-16 im Zuge der Doku-Aufräum-Session (`docs/doku-cleanup-session.md`, Punkt D).*

View File

@@ -0,0 +1,8 @@
# === BF Core-Switch sw1 (ALL-SG8316M, FW 2.2.1) — VLAN-Datenbank ===
# Reproduzierbar: . switch/.secrets && expect switch/cli/run-cli.exp switch/cli/01-vlan-db.cli
# NON-DISRUPTIV: legt nur VLANs an; ändert KEINE Port-Zugehörigkeit (alle Ports bleiben default VLAN1).
# VID-Plan: 10=Server/Main 20=Docker 30=IoT 40=Guest 100=WAN
configure
vlan 10,20,30,40,100
end
save

View File

@@ -0,0 +1,30 @@
# === BF Core-Switch sw1 (ALL-SG8316M, FW 2.2.1) — VLAN-Namen setzen ===
# Reproduzierbar: . switch/.secrets && expect switch/cli/02-vlan-names.exp
# NON-DISRUPTIV: benennt nur die bereits angelegten VLANs (aus 01-vlan-db.cli);
# ändert KEINE Port-Zugehörigkeit.
# VID-Plan: 10=MAIN 20=INFRA 30=IOT 40=UNTRUSTED 90=GUEST 99=MGMT 100=WAN
configure
vlan 10
name MAIN
exit
vlan 20
name INFRA
exit
vlan 30
name IOT
exit
vlan 40
name UNTRUSTED
exit
vlan 90
name GUEST
exit
vlan 99
name MGMT
exit
vlan 100
name WAN
exit
end
save
show vlan

View File

@@ -0,0 +1,107 @@
#!/usr/bin/expect -f
# VLAN-Namen setzen — korrigiert: `end` aus config-vlan geht zu sw1#,
# also nicht nutzen. Stattdessen `exit` aus config-vlan → config,
# und einmal `end` zum Speichern am Ende.
set timeout 30
log_user 1
spawn telnet $env(SW_HOST)
expect "Username:"
send "$env(SW_USER)\r"
expect "Password:"
sleep 1
send "$env(SW_PASS)\r"
expect "sw1#"
send "configure\r"
expect "(config)"
# 90 GUEST
send "vlan 90\r"
expect "(config-vlan)"
send "name GUEST\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# 99 MGMT
send "vlan 99\r"
expect "(config-vlan)"
send "name MGMT\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# 10 MAIN
send "vlan 10\r"
expect "(config-vlan)"
send "name MAIN\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# 20 INFRA
send "vlan 20\r"
expect "(config-vlan)"
send "name INFRA\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# 30 IOT
send "vlan 30\r"
expect "(config-vlan)"
send "name IOT\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# 40 UNTRUSTED
send "vlan 40\r"
expect "(config-vlan)"
send "name UNTRUSTED\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# 100 WAN
send "vlan 100\r"
expect "(config-vlan)"
send "name WAN\r"
expect "(config-vlan)"
send "exit\r"
expect "(config)"
# Save!
send "end\r"
expect "sw1#"
send "save\r"
expect {
"Success" { send "\r" }
timeout { }
}
# Verifizieren mit show vlan (paged)
send "show vlan\r"
# --More-- handling
for {set i 0} {$i < 20} {incr i} {
expect {
-ex "--More--" {
sleep 0.3
send " "
}
-ex "sw1#" {
break
}
timeout { break }
}
}
send "exit\r"
expect eof

View File

@@ -0,0 +1,46 @@
spawn telnet 10.40.41.239
Trying 10.40.41.239...
Connected to sw1.fritz.box.
Escape character is '^]'.
Username: claudecode
Password: ************
sw1# configure
sw1(config)# vlan 90
sw1(config-vlan)# name GUEST
sw1(config-vlan)# exit
sw1(config)# vlan 99
sw1(config-vlan)# name MGMT
sw1(config-vlan)# exit
sw1(config)# vlan 10
sw1(config-vlan)# name MAIN
sw1(config-vlan)# exit
sw1(config)# vlan 20
sw1(config-vlan)# name INFRA
sw1(config-vlan)# exit
sw1(config)# vlan 30
sw1(config-vlan)# name IOT
sw1(config-vlan)# exit
sw1(config)# vlan 40
sw1(config-vlan)# name UNTRUSTED
sw1(config-vlan)# exit
sw1(config)# vlan 100
sw1(config-vlan)# name WAN
sw1(config-vlan)# exit
sw1(config)# end
sw1# save
Success
sw1#
sw1# show vlan
VID | VLAN Name | Untagged Ports | Tagged Ports | Type
-------+------------------+------------------------------+------------------------------+---------
1 | default | gi1-16,lag1-8 | --- | Default
10 | MAIN | --- | --- | Static
20 | INFRA | --- | --- | Static
30 | IOT | --- | --- | Static
40 | UNTRUSTED | --- | --- | Static
90 | GUEST | --- | --- | Static
99 | MGMT | --- | --- | Static
100 | WAN | --- | --- | Static
sw1# exit
sw1>

View File

@@ -0,0 +1,21 @@
# === BF Core-Switch sw1 (ALL-SG8316M, FW 2.2.1) — Trunk-Ports für hAP-APs ===
# Reproduzierbar: . switch/.secrets && expect switch/cli/run-cli.exp switch/cli/03-trunk-ap-ports.cli
# Belegung (provisorisch, 2026-06-09): gi1 = ap1 (10.40.41.1), gi2 = ap2 (10.40.41.2).
# Absicht: native vlan1 untagged (Mgmt/Flachnetz bleibt, kein Lockout) + tagged 10,20,30,40,90,99,100.
# NICHT-DISRUPTIV: nur AP-Ports (tragen keinen Haushalts-Traffic); vlan1 bleibt überall untagged.
# Syntax am Gerät verifiziert: 'switchport trunk allowed vlan add <LIST>' / 'native vlan <id>'.
# Angewendet + verifiziert (POST show vlan: gi1-2 tagged auf 10..100; vlan1 weiter untagged). save=Success.
configure
interface gi1
switchport mode trunk
switchport trunk native vlan 1
switchport trunk allowed vlan add 10,20,30,40,90,99,100
exit
interface gi2
switchport mode trunk
switchport trunk native vlan 1
switchport trunk allowed vlan add 10,20,30,40,90,99,100
exit
end
save
show vlan

View File

@@ -0,0 +1,23 @@
# === BF Core-Switch sw1 (ALL-SG8316M, FW 2.2.1) — VLAN 40→MEDIA + VLAN 50 UNTRUSTED ===
# Reproduzierbar: . switch/.secrets && expect switch/cli/run-cli.exp switch/cli/04-vlan50-media-untrusted.cli
# NON-DISRUPTIV:
# - VLAN 40 wird nur umbenannt (kein Port-Eingriff, Membership bleibt identisch)
# - VLAN 50 wird angelegt + benannt (noch kein Port-Member → kein Traffic betroffen)
# - GE1 + GE2 (AP-Trunks) bekommen VLAN 50 als tagged hinzu (kein Gerät nutzt VLAN 50 noch)
# Ergebnis: VLAN-DB korrekt (40=MEDIA, 50=UNTRUSTED), AP-Trunks bereit für künftige UNTRUSTED-SSID.
configure
vlan 40
name MEDIA
exit
vlan 50
name UNTRUSTED
exit
interface gi1
switchport trunk allowed vlan add 50
exit
interface gi2
switchport trunk allowed vlan add 50
exit
end
save
show vlan

View File

@@ -0,0 +1,55 @@
# ALL-SG8316M FW 2.2.1.2956 — CLI-Referenz (Telnet)
Switch `sw1` @ 10.40.41.239. **Kein SSH** → Telnet (Port 23). IOS-artige CLI. Login: `claudecode` (Creds in `switch/.secrets`).
Prompt-Ebenen: `sw1>` (user) → `sw1#` (enable) → `sw1(config)#` (config). `configure` rein, `end`/`exit` raus.
> ⚠️ **Nur EINE VTY-Line!** Der Switch reapt halb-offene Sessions schlecht (stale Session sperrt ~15 min).
> → Immer **serialisiert** arbeiten und **sauber ausloggen** (`exit` bis zur Verbindungstrennung). Bei „busy" **nicht hämmern**, paar Minuten warten.
Tooling (macOS): `telnet` (/opt/homebrew/bin) + `expect`. Python 3.14 hat KEIN telnetlib. Runner: `switch/cli/run-cli.exp <file.cli>`.
## Management-IP
- **Ansehen** (enable): `show ip` → IP / Subnet Netmask / Default Gateway.
- `show ip ?`: `dhcp dns http https igmp` (+ `<cr>` = Summary).
- **Statisch setzen** (config):
```
ip address <A.B.C.D> mask <A.B.C.D>
ip default-gateway <A.B.C.D>
```
- ✅ **Netzmasken-Syntax bestätigt:** `mask 255.255.255.0` (aus running-config abgelesen).
- DHCP statt statisch: `ip dhcp`.
- **NICHT vorhanden:** `interface vlan`, `show ip interface`, `show management`, `terminal datadump`.
## Management-VLAN
- **Setzen** (config): `management-vlan vlan <1-4094>` (`management` = Abk.)
- **Ansehen** (enable): `show management-vlan` → aktuell `default(1)`.
- Reihenfolge-Warnung: Ziel-VLAN muss existieren UND der Mgmt-Port muss es tragen, sonst Reachability weg.
## VLANs
- **Anlegen** (config): `vlan <liste>` z.B. `vlan 10,20,30,40,100` (Range 1–4094, `3,6-8` möglich).
- **Ansehen** (enable): `show vlan`.
- **Namen setzen** (config):
```
configure
vlan <id> → sw1(config-vlan)#
name <NAME> → max 32 Zeichen, keine Leerzeichen
exit → zurück zu sw1(config)#
```
- ⚠️ **`end` aus config-vlan geht DIREKT zu sw1#** (überspringt config-Ebene) — also `exit` benutzen, nicht `end`, wenn man weitere VLANs bearbeiten will.
- Befehle im config-vlan: `do` (exec-Befehle), `end`/`exit` (raus), `name` (Name setzen).
- Aktuelle Namen: 10=MAIN, 20=INFRA, 30=IOT, 40=MEDIA, 50=UNTRUSTED, 90=GUEST, 99=MGMT, 100=WAN.
## Ports (gi1-16, lag1-8) — Trunk/Access (für Cutover)
```
interface gi<n> (oder: interface range ...)
switchport mode {access|hybrid|trunk}
switchport access vlan <id>
switchport trunk native vlan <id>
switchport trunk allowed vlan ... (genaue add/remove-Syntax via `switchport trunk allowed ?` prüfen)
```
## Speichern / Diverses
- **Persistieren:** `save` (enable) → „Success". (Sonst nach Reboot weg!)
- **Config-Dump:** `show running-config` / `show startup-config` (Paging `--More--` → Space).
- **Version:** `show version` → `Firmware Version : 2.2.1.2956`.
- Andere: `reboot`, `restore-defaults`, `copy`, `clock`, `ping`, `ssl`, `clear`, `disable`, `terminal`.

47
switch/cli/backup.exp Normal file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/expect -f
# Backup running-config mit --More-- Handling
# Schreibt komplette running-config nach STDOUT (zur Weiterleitung in Datei)
set timeout 60
log_user 1
spawn telnet $env(SW_HOST)
expect "Username:"
send "$env(SW_USER)\r"
expect "Password:"
sleep 1
send "$env(SW_PASS)\r"
expect {
"sw1#" { }
timeout { puts "TIMEOUT: kein Prompt"; exit 1 }
}
send "show running-config\r"
# Seiten durchblättern bis zurück zum Prompt
set more_count 0
while {$more_count < 100} {
expect {
-ex "--More--" {
set more_count [expr $more_count + 1]
sleep 0.3
send " "
}
-ex "sw1#" {
break
}
timeout {
puts "TIMEOUT beim Paging"
break
}
}
}
send "\r"
expect "sw1#"
send "exit\r"
expect eof

33
switch/cli/run-cli.exp Normal file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/expect -f
# Generischer Telnet-CLI-Runner für sw1 (ALL-SG8316M, FW 2.2.1, IOS-artige CLI).
# Nutzung: . switch/.secrets && expect switch/cli/run-cli.exp switch/cli/<datei>.cli
# Liest die .cli-Datei zeilenweise, loggt ein, sendet jeden Befehl, flaggt Fehler.
# Zeilen mit führendem '#' = Kommentar (übersprungen). Am Ende: show vlan zur Kontrolle.
set timeout 20
set user $env(SW_USER)
set pass $env(SW_PASS)
set host $env(SW_HOST)
if {[llength $argv] < 1} { puts "usage: run-cli.exp <file.cli>"; exit 1 }
set fh [open [lindex $argv 0] r]
log_user 1
spawn telnet $host
expect "Username:"; send "$user\r"
expect "Password:"; send "$pass\r"
expect -re {sw1[#>]}
while {[gets $fh line] >= 0} {
set line [string trim $line]
if {$line eq "" || [string match "#*" $line]} { continue }
send "$line\r"
expect {
"Incomplete command" { puts "\n>>> FEHLER (incomplete): $line" }
"Unknown command" { puts "\n>>> FEHLER (unknown): $line" }
-re {sw1\([^)]*\)[#>] ?$} {}
-re {sw1[#>] ?$} {}
timeout { puts "\n>>> TIMEOUT bei: $line" }
}
}
close $fh
send "show vlan\r"
expect -re {sw1[#>]}
send "exit\r"
expect eof

41
switch/recovery/README.md Normal file
View File

@@ -0,0 +1,41 @@
# Mac-mini Recovery-Anker für den Switch-Flash
**Zweck:** Der Mac mini (10.40.41.167, en0, L2-benachbart zum Switch) trägt dauerhaft eine
**Zweit-IP 192.168.1.2/24**. Nach einem Firmware-Flash kommt der Switch auf `192.168.1.1` hoch
(Werks-Default) — über die Zweit-IP erreicht Claude ihn **vom Mac mini aus** und setzt ihn
automatisch auf `10.40.41.239` zurück. Kein manuelles IP-Suchen, kein Laptop nötig.
## Variante A — nur für heute (transient, kein Reboot dazwischen)
Auf dem Mac mini (deine SSH-Session):
```
sudo ifconfig en0 alias 192.168.1.2 255.255.255.0
ifconfig en0 | grep 192.168.1 # prüfen
```
Entfernen: `sudo ifconfig en0 -alias 192.168.1.2`
## Variante B — persistent (übersteht Reboots, empfohlen wenn Dauer-Anker)
Auf dem Mac mini, aus diesem Repo-Ordner (`switch/recovery/`):
```
sudo cp switch-recovery-ip.sh /usr/local/sbin/switch-recovery-ip.sh
sudo chmod 755 /usr/local/sbin/switch-recovery-ip.sh
sudo cp com.bf.switchrecoveryip.plist /Library/LaunchDaemons/
sudo chown root:wheel /Library/LaunchDaemons/com.bf.switchrecoveryip.plist
sudo launchctl load -w /Library/LaunchDaemons/com.bf.switchrecoveryip.plist
ifconfig en0 | grep 192.168.1 # prüfen
```
Deinstallieren:
```
sudo launchctl unload -w /Library/LaunchDaemons/com.bf.switchrecoveryip.plist
sudo rm /Library/LaunchDaemons/com.bf.switchrecoveryip.plist /usr/local/sbin/switch-recovery-ip.sh
sudo ifconfig en0 -alias 192.168.1.2
```
## Recovery-Ablauf nach Flash (Claude führt aus, vom Mac mini)
```
ssh 10.40.41.167
cd ~/.../bf-network-r6/switch # Repo muss auf dem Mac mini liegen ODER Claude tunnelt
SW_HOST=192.168.1.1 SW_USER=admin SW_PASS=admin ./sg8316m.sh set-ip 10.40.41.239
# Switch nun wieder auf .239 → .secrets zurück, dann VLANs/Ports/save
```
> Hinweis: Claude erreicht den Switch nach Flash **nur** über den Mac mini (.1.1 ist L2-lokal am Mac mini).
> Der Treiber `sg8316m.sh` muss daher vom Mac mini aus laufen (Repo dorthin klonen/kopieren oder via SSH ausführen).

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.bf.switchrecoveryip</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/sbin/switch-recovery-ip.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/var/log/switch-recovery-ip.log</string>
</dict>
</plist>

View File

@@ -0,0 +1,136 @@
#!/bin/sh
# Autonome Flash- + Recovery-Orchestrierung für ALL-SG8316M.
# LÄUFT AUF DEM MAC MINI (10.40.41.167, en0, L2-lokal zum Switch).
# Zweck: Claude stößt es detached an und ist während des Switch-Reboots OFFLINE.
# Das Script zieht die ganze Kette autonom durch und loggt alles.
#
# Kette: pre-flight -> flash -> warte down -> warte up(.1.1|.239)
# -> (falls .1.1) IP zurück auf .239 [cmd dynamisch entdeckt]
# -> VLANs (best-effort) -> save-to-flash -> verify
#
# Aufruf: ./flash-and-recover.sh preflight # nur Checks, kein Flash
# ./flash-and-recover.sh run # echter Flash + Recovery
set -u
SW_PROD=10.40.41.239
SW_DEFAULT=192.168.1.1
PROD_MASK=255.255.255.0
PROD_GW=10.40.41.254
ALIAS_IP=192.168.1.2
VLANS=10,20,30,40,100
IMG="${IMG:-$HOME/bf-switch-flash/all-sg8316m-version_2.2.1_vmlinux.bix}"
IMG_SHA=e3f160983e8b17962dc70a52360b7f560c92da8197928d702f5266027a0fc7fc
LOG="${LOG:-$HOME/bf-switch-flash/flash-$(date +%Y%m%d-%H%M%S).log}"
MODE="${1:-preflight}"
mkdir -p "$(dirname "$LOG")"
log(){ printf '%s %s\n' "$(date '+%H:%M:%S')" "$*" | tee -a "$LOG"; }
# Liveness: Webserver-Root antwortet (firmware-stabiler als ein bestimmter cmd)
http_code(){ curl -s -o /dev/null -w '%{http_code}' --max-time 5 "http://$1/" 2>/dev/null; }
up(){ [ "$(http_code "$1")" = "200" ]; }
fw_version(){ curl -s --max-time 6 "http://$1/cgi-bin/dispatcher.cgi?cmd=512" \
| sed -e 's/<[^>]*>/ /g' | grep -oiE 'Firmware Version[^0-9]*[0-9.]+' | head -1; }
wait_up(){ _e=$(( $(date +%s) + $2 )); while [ "$(date +%s)" -lt "$_e" ]; do up "$1" && return 0; sleep 4; done; return 1; }
wait_down(){ _e=$(( $(date +%s) + $2 )); while [ "$(date +%s)" -lt "$_e" ]; do up "$1" || return 0; sleep 4; done; return 1; }
# IP zurücksetzen — cmd + Seite dynamisch aus der Live-UI entdecken (firmware-drift-sicher)
restore_ip(){
_host=$1
_menu=$(curl -s --max-time 8 "http://$_host/cgi-bin/dispatcher.cgi?cmd=2")
_ipcmd=$(printf '%s' "$_menu" | grep -i 'IP Address' | grep -oE 'cmd=[0-9]+' | head -1 | cut -d= -f2)
[ -z "$_ipcmd" ] && _ipcmd=517
_form=$(curl -s --max-time 8 "http://$_host/cgi-bin/dispatcher.cgi?cmd=$_ipcmd")
_post=$(printf '%s' "$_form" | grep -i 'name="cmd"' | grep -oE 'value="[0-9]+"' | head -1 | grep -oE '[0-9]+')
[ -z "$_post" ] && _post=518
log "restore_ip: ip-page=$_ipcmd post-cmd=$_post -> set $SW_PROD"
curl -s --max-time 10 -X POST "http://$_host/cgi-bin/dispatcher.cgi" \
--data "mode=0" --data "ip=$SW_PROD" --data "netmask=$PROD_MASK" \
--data "gateway=$PROD_GW" --data "cmd=$_post" >/dev/null 2>&1
}
add_vlans(){ curl -s --max-time 10 -X POST "http://$1/cgi-bin/dispatcher.cgi" \
--data "vlanlist=$VLANS" --data "vlanAction=0" --data "name=" --data "cmd=1284" >/dev/null 2>&1; }
save_flash(){ curl -s --max-time 15 "http://$1/cgi-bin/dispatcher.cgi?cmd=4358" >/dev/null 2>&1; }
# ---------- PRE-FLIGHT ----------
log "=== PRE-FLIGHT (mode=$MODE) ==="
fail=0
if [ -f "$IMG" ]; then
h=$(shasum -a 256 "$IMG" 2>/dev/null | awk '{print $1}')
if [ "$h" = "$IMG_SHA" ]; then log "OK image sha256"; else log "FAIL image sha256: $h"; fail=1; fi
else log "FAIL image missing: $IMG"; fail=1; fi
if ifconfig en0 2>/dev/null | grep -q "$ALIAS_IP"; then log "OK recovery-alias $ALIAS_IP on en0";
else log "FAIL alias $ALIAS_IP missing -> sudo ifconfig en0 alias $ALIAS_IP 255.255.255.0"; fail=1; fi
if up "$SW_PROD"; then log "OK switch up at $SW_PROD (FW: $(fw_version "$SW_PROD"))";
else log "FAIL switch not reachable at $SW_PROD"; fail=1; fi
if [ "$fail" -ne 0 ]; then log "PRE-FLIGHT FAILED -> abort"; exit 2; fi
log "PRE-FLIGHT OK"
[ "$MODE" = "preflight" ] && { log "preflight-only -> done (kein Flash)"; exit 0; }
# ---------- FLASH ----------
MARKER="$HOME/bf-switch-flash/.flashed-2.2.1"
if [ -f "$MARKER" ] && up "$SW_PROD"; then
log "Marker vorhanden + Switch up @ $SW_PROD -> Flash übersprungen (nur reconfig)"; skip=1
else skip=0; fi
if [ "$skip" -eq 0 ]; then
log "=== FLASH: lade Image hoch ($IMG) ==="
curl -s --max-time 200 -F "type=0" -F "http_file=@$IMG" -F "cmd=4364" \
"http://$SW_PROD/cgi-bin/httpupload.cgi" >>"$LOG" 2>&1
log "Upload abgeschlossen, warte auf Reboot (down)..."
# Der Reboot selbst ist das verlässliche Flash-Signal (Flash erzwingt Neustart).
if wait_down "$SW_PROD" 180; then
log "Switch down (Reboot) — Flash hat gegriffen"
else
log "FAIL kein Reboot binnen 180s — Flash vermutlich nicht angenommen (Upload/Format/Auth?)."
log " Switch unverändert @ $SW_PROD, Netz intakt, KEIN Reset. Abbruch."
exit 3
fi
fi
# ---------- WARTEN AUF WIEDERKEHR ----------
log "=== RECOVERY: warte auf Switch (max 6 min, .1.1 ODER .239) ==="
target=""; _e=$(( $(date +%s) + 360 ))
while [ "$(date +%s)" -lt "$_e" ]; do
if up "$SW_DEFAULT"; then target=$SW_DEFAULT; break; fi
if up "$SW_PROD"; then target=$SW_PROD; break; fi
sleep 4
done
if [ -z "$target" ]; then
log "FAIL Switch kam binnen 6min nicht auf $SW_DEFAULT noch $SW_PROD zurück."
log " -> MANUELLE Recovery nötig (Reset-Knopf 10s, dann erneut)."
exit 4
fi
log "Switch zurück an $target"
# ---------- IP WIEDERHERSTELLEN ----------
if [ "$target" = "$SW_DEFAULT" ]; then
log "Switch auf Default $SW_DEFAULT -> stelle $SW_PROD wieder her"
restore_ip "$SW_DEFAULT"
if wait_up "$SW_PROD" 60; then
log "OK Switch wieder auf $SW_PROD"
else
log "FAIL set-ip griff nicht — FW 2.2.1 erzwingt vermutlich Login (FW1.0 war offen)."
log " LAN/Internet laufen TROTZDEM (L2-Forwarding). Nur die Mgmt-IP hängt auf $SW_DEFAULT."
log " MANUELL: Browser http://$SW_DEFAULT Login admin/admin -> Network/IP Address"
log " -> $SW_PROD Maske $PROD_MASK GW $PROD_GW -> Save to Flash."
exit 5
fi
else
log "Switch hat $SW_PROD behalten (Firmware bewahrte IP)"
fi
# ---------- RECONFIG (best-effort, unkritisch — online nachholbar) ----------
log "=== reconfig (best-effort) ==="
if add_vlans "$SW_PROD"; then log "VLANs $VLANS abgeschickt"; else log "WARN VLAN-Add fehlgeschlagen (online nachholen)"; fi
if save_flash "$SW_PROD"; then log "save-to-flash abgeschickt"; else log "WARN save fehlgeschlagen (online nachholen!)"; fi
# ---------- VERIFY ----------
if up "$SW_PROD"; then
touch "$HOME/bf-switch-flash/.flashed-2.2.1"
log "=== SUCCESS: Switch up @ $SW_PROD (Marker gesetzt) ==="
exit 0
else
log "=== WARN: Switch am Ende nicht bestätigt up ==="
exit 6
fi

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# Recovery-Anker: Zweit-IP 192.168.1.2/24 auf en0, damit der Mac mini den
# ALL-SG8316M nach einem Firmware-Flash (Reset auf 192.168.1.1) erreichen kann.
# Wird per LaunchDaemon bei jedem Boot ausgeführt (retry bis en0 da ist).
IFACE=en0
IP=192.168.1.2
MASK=255.255.255.0
i=0
while [ "$i" -lt 10 ]; do
if ifconfig "$IFACE" >/dev/null 2>&1; then
ifconfig "$IFACE" alias "$IP" "$MASK" 2>/dev/null && exit 0
fi
i=$((i+1)); sleep 3
done
exit 1

61
switch/sg8316m-cgi-map.md Normal file
View File

@@ -0,0 +1,61 @@
# ALL-SG8316M — dispatcher.cgi API-Map (Recon 2026-06-05)
> ⚠️ **VERALTET — nur FW 1.0.** Switch ist auf **FW 2.2.1** geflasht; die Web-UI/cmd-Codes hier gelten
> NICHT mehr (cmd=2 → 404). Konfiguration jetzt via **Telnet-CLI** → `switch/cli/` (run-cli.exp + .cli).
> Dieses Dokument bleibt nur als Historie.
Switch: **10.40.41.239** · Web-UI: Realtek **`/cgi-bin/dispatcher.cgi?cmd=<N>`** · `Server: Hydra/0.1.8` (FW-Basis 2014).
Reine **CGI/Form-POST**-UI (kein JS-SPA) → **vollständig per `curl` skriptbar**. Von diesem Mac direkt erreichbar (~7 ms),
ebenso vom Mac mini (10.40.41.167). **Kein SSH** am Switch; **Telnet** existiert als Option (cmd=534), aber curl ist sauberer.
> ⚠️ Auth-Hinweis: Unauthentifizierte GETs liefern bereits Config-Seiten (bekannte Schwäche dieser Realtek-dispatcher.cgi-Boxen).
> Login-Mechanik vor Schreibzugriff verifizieren. **Konsequenz fürs Design:** Mgmt strikt auf vlan10/restricted halten,
> EOL-Gerät (2014) nie Richtung WAN exponieren.
## Befehlscodes (aus Menü cmd=2 extrahiert)
### 🎯 Für R6-Config gebraucht
| cmd | Seite | Zweck |
|---|---|---|
| **1282** | Create VLAN | VLANs 10/20/30/40/100 anlegen |
| **1289** | Interface Settings | Port-Modus (Access/Trunk/Hybrid) + PVID |
| **1291** | Port to VLAN | VLAN↔Port-Matrix |
| **1293** | Port VLAN Membership | tagged/untagged je Port |
| **1297** | Management VLAN | Mgmt-VLAN 1→10 (Cutover) |
| **517** | IP Address | Switch-Mgmt-IP (Ziel 10.40.41.2) |
| **524** | Account Manager | eigenen User anlegen |
| **534/537/539** | Telnet/HTTP/HTTPS | Zugangsdienste |
### 🔧 Maintenance
| cmd | Seite |
|---|---|
| 4361 | Backup Manager (User: Export defekt → wir scrapen stattdessen, s.u.) |
| 4363 | Upgrade Manager (**Firmware**) |
| 4358 | Configuration Manager / Save-to-Flash |
| 524 | Account Manager |
### ⛔ NICHT aufrufen (destruktiv/disruptiv)
| cmd | Wirkung |
|---|---|
| 4352 | **Reboot** |
| 4354 | **Factory Default / Restore Defaults** |
| 4358 | Save to FLASH — nur bewusst am Ende eines Config-Laufs |
### Weitere (Referenz)
Status 512(SysInfo) 3848(Log) · Port 779/774/1031/4619/1814 · Net 517/519/1297 · Time 541/543 ·
Switching 768(Port) 2048(Mirror) · LAG 1024/1026/1032/1034/1036 · VoiceVLAN 1536/1538/1543 3328(EEE) ·
Multicast 1792 · IGMP 1802/1806/1797/1796/1808 3072(Jumbo) · STP 4096/4098/4100/4102/4108 ·
MAC 2311/2309/2304 · Security: Storm 3586/3584 772(Protected) DoS 4864/4866 · Access 534/537/539 ·
QoS 2560/2562/2564/2566/2568/2570 Basic 2572/2574 RateLimit 3588/3590/3592 ·
LLDP 4608/4610/4617/4614/4621 · SNMP 2816/2818/2821 · Diag 5120/527/530 · Logging 3840/3842/3844 ·
Maint 4361/4363/4358/524 4354/4352.
## „Backup" trotz defektem Export
User: Config-Import/Export der FW ist kaputt. **Kein Blocker:** Wir GETten jede Config-Seite per curl in Textdateien
(`switch/state-PRE/*.html`) → **git-versionierter, menschenlesbarer Ist-Zustand** statt opaker Binär-Blob. Restore =
gezielte CGI-POSTs (nicht Blob-Reupload). Damit ist der Vorgang reproduzierbar + diff-bar.
## Skript-Plan (statt MCP)
`switch/sg8316m.sh` (bash+curl) oder `.py` (requests): login→Session-Cookie → State-Scrape (PRE) →
VLANs anlegen (1282) → Interface/Membership (1289/1293) → optional Mgmt-VLAN/IP (1297/517) → Save (4358).
Idempotent, dry-run-fähig, im Repo versioniert. Läuft von hier oder vom Mac mini.

72
switch/sg8316m.sh Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# ⚠️ VERALTET — nur FW 1.0. Switch ist auf FW 2.2.1 geflasht; die dispatcher.cgi-cmd-Codes
# unten gelten dort NICHT mehr. Konfig jetzt via Telnet-CLI: switch/cli/ (run-cli.exp + .cli).
# Bleibt nur als Historie / für den (unwahrscheinlichen) FW-1.0-Rollback.
# ALL-SG8316M (Realtek dispatcher.cgi) driver — curl-based, no MCP needed.
# Read ops are safe. WRITE ops change config; SAVE persists to flash.
# Creds via ./.secrets (gitignored): SW_HOST, SW_USER, SW_PASS.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
[ -f "$HERE/.secrets" ] && . "$HERE/.secrets"
HOST="${SW_HOST:-10.40.41.239}"
BASE="http://$HOST/cgi-bin/dispatcher.cgi"
CURL=(curl -sS --max-time 12)
# --- read ---
get() { "${CURL[@]}" "$BASE?cmd=$1"; }
# Curated config-view + AJAX-data cmd codes (see sg8316m-cgi-map.md)
SCRAPE_CMDS="512 517 519 1297 541 543 768 779 1024 1026 1031 \
1282 1283 1289 1291 1293 534 537 539 524 \
4096 4098 4100 2560 2562 2564 2572 2574 2816 2818 2821 3840 3842 3844"
scrape() {
local out="$HERE/${1:-state-PRE}"; mkdir -p "$out"
echo "Scraping $HOST -> $out"
for c in $SCRAPE_CMDS; do
if "${CURL[@]}" "$BASE?cmd=$c" >"$out/cmd-$c.html" 2>/dev/null; then
printf ' cmd=%-5s %6s bytes\n' "$c" "$(wc -c <"$out/cmd-$c.html" | tr -d ' ')"
else
printf ' cmd=%-5s FAILED\n' "$c"
fi
done
}
# Pretty-print one page as text
view() { get "$1" | sed -e 's/<[^>]*>/ /g' | tr -s ' \t\r\n' ' ' | sed 's/&nbsp;/ /g'; echo; }
# --- write (use deliberately, then `save`) ---
post() { "${CURL[@]}" -X POST "$BASE" "$@"; }
add_vlans() { post --data-urlencode "vlanlist=$1" --data "vlanAction=0" --data-urlencode "name=${2:-}" --data "cmd=1284"; }
del_vlans() { post --data-urlencode "vlanlist=$1" --data "vlanAction=1" --data "cmd=1284"; }
# set_ip <ip> [netmask] [gateway] — RECOVERY: re-point mgmt IP (e.g. after flash reset to 192.168.1.1)
set_ip() { post --data "mode=0" --data "ip=$1" --data "netmask=${2:-255.255.255.0}" --data "gateway=${3:-10.40.41.254}" --data "cmd=518"; }
save_flash() { echo ">> SAVE to FLASH (cmd=4358)"; get 4358 >/dev/null; }
usage() { cat <<USAGE
usage: $0 <cmd>
scrape [dir] dump config pages to switch/<dir> (default state-PRE) [READ]
get <N> raw HTML of cmd=N [READ]
view <N> cmd=N as plain text [READ]
add-vlans <list> [prefix] e.g. add-vlans 10,20,30,40,100 [WRITE]
del-vlans <list> [WRITE]
set-ip <ip> [mask] [gw] RECOVERY: re-point mgmt IP [WRITE]
save persist running config to flash [WRITE]
Recovery after flash (switch reset to 192.168.1.1):
SW_HOST=192.168.1.1 SW_USER=admin SW_PASS=admin $0 set-ip 10.40.41.239
USAGE
}
case "${1:-}" in
scrape) scrape "${2:-state-PRE}" ;;
get) get "$2" ;;
view) view "$2" ;;
add-vlans) add_vlans "$2" "${3:-}" ;;
del-vlans) del_vlans "$2" ;;
set-ip) set_ip "$2" "${3:-255.255.255.0}" "${4:-10.40.41.254}" ;;
save) save_flash ;;
*) usage ;;
esac

View File

@@ -0,0 +1,79 @@
# ALLNET ALL-SG8316M — Web-UI-Guide: Trunk-Port für einen hAP-AP
> ✅ **STATUS 2026-06-09: gi1 (ap1) + gi2 (ap2) bereits getrunkt** — via Telnet-CLI ausgeführt
> (`switch/cli/03-trunk-ap-ports.cli`), `show vlan` POST verifiziert (gi1-2 tagged auf 10/20/30/40/90/99/100,
> vlan1 weiter untagged), `save` ok, Mgmt + APs erreichbar. Diese UI-Anleitung bleibt als **Referenz/Verifikation**
> und Vorlage für künftige Ports. Syntax `switchport trunk allowed vlan add <LIST>` am Gerät bestätigt.
> **Methode:** Claude erklärt Absicht + Klickpfad, **du klickst**. UI = Realtek-OEM (Backbone-SPA),
> FW 2.2.1.2956, erreichbar unter **https://10.40.41.239** (Login `claudecode`, PW in `switch/.secrets`).
> Labels unten sind aus der echten `lang/defLang.js` des Geräts gezogen — sollten exakt passen;
> falls eine Bezeichnung leicht abweicht, geht's um die **Absicht** (steht je Schritt dabei).
## Was wir wollen (Absicht in einem Satz)
Den Switch-Port, an dem ein hAP hängt, zu einem **Trunk** machen:
**VLAN 1 untagged (native)** = Mgmt/aktuelles Flachnetz bleibt drauf, **VLANs 10,20,30,40,90,99,100 tagged**.
So trägt der Port später die SSID→VLAN-Tags (Phase B), **und das jetzige Flach-WLAN „BF-Main" auf vlan1 läuft unverändert weiter.**
### CLI-Äquivalent (nur zum Gegenchecken — du machst es im UI)
```
interface gi<PORT>
switchport mode trunk
switchport trunk native vlan 1
switchport trunk allowed vlan add 10,20,30,40,90,99,100
```
## ⚠️ Bevor du klickst — Sicherheit (Familiennetz läuft!)
- **NUR den AP-Uplink-Port anfassen.** Finger weg von **Port 4, 8, 15, 16** (live: Wohnzimmer-/Arbeitszimmer-Switch, pve, Fritzbox).
- **VLAN 1 bleibt überall untagged** → die Switch-Mgmt-IP (.239, vlan1) bleibt erreichbar. **Kein Lockout.**
- **Management VLAN NICHT ändern** (bleibt 1). Das ist ein separater Cutover-Schritt mit Aussperr-Risiko.
- Welcher Port ist der AP? Laut Plan **ap1 = Port 2** (direkt am Core). **Bitte bestätigen** und unten `<PORT>` einsetzen.
→ **ap2** hängt aktuell hinter dem TL-SG108E an **Port 8 (live!)** — **diesen Port NICHT trunken.** Für ap2 siehe Abschnitt unten.
- Nicht dringend: Fürs aktuelle Flach-WLAN-Testen ist **gar keine** Switch-Änderung nötig. Dieser Trunk ist **Phase-B-Vorbereitung** (taggte VLANs bekommen erst nach dem Router-Cutover Gateway/DHCP).
## Klickpfad
### 0) Login & Vorprüfung
1. Browser → **https://10.40.41.239** → Login `claudecode`.
2. Menü **VLAN → Create VLAN**: prüfen, dass **10, 20, 30, 40, 90, 99, 100** in „Created VLAN" stehen.
(Sollten schon da sein — VLAN-DB + Namen sind angelegt. Falls eine fehlt: ID eintragen → **Add/Apply**.)
### 1) Port-Modus & PVID setzen — `VLAN → Port Setting`
1. Menü **VLAN → Port Setting** (Tabelle „Port Setting Table").
2. Zeile **Port `<PORT>`** auswählen → **Edit** (öffnet „Edit Port Setting").
3. Felder:
- **Mode** = **Trunk** *(falls „Trunk" zickt, geht auch **Hybrid** — alle Ports stehen eh auf Hybrid; Hybrid + Membership unten erreicht dasselbe.)*
- **PVID** = **1** *(untagged-Verkehr landet in vlan1 = Mgmt/Flachnetz)*
- **Accept Frame Type** = **All** *(tagged + untagged annehmen)*
- **Ingress Filtering** = **Enabled** *(nur Frames erlaubter VLANs — sauber; optional)*
4. **Apply/OK**.
### 2) VLAN-Mitgliedschaft des Ports — `VLAN → Membership`
Hier legst du pro VLAN fest, wie Port `<PORT>` Mitglied ist: **U**ntagged / **T**agged / leer (kein Mitglied).
1. Menü **VLAN → Membership** (Tabelle „Membership Table", Achse VLAN × Port).
2. **VLAN 1** editieren → Port `<PORT>` = **Untagged (U)** *(native; Mgmt/Flachnetz)*
3. **VLAN 10** editieren → Port `<PORT>` = **Tagged (T)**. Ebenso **20, 30, 40, 90, 99, 100** = **Tagged (T)**.
*(In der „Edit"-Maske oft per Klick durch die Zustände U → T → leer, oder Auswahl „Active/Tagged Member".)*
4. Sicherstellen: Port `<PORT>` ist bei den **anderen** VLANs **nicht** ungewollt untagged.
5. **Apply/OK** je VLAN.
### 3) Persistieren (sonst nach Reboot weg!)
- Oben rechts das **Save**-Symbol (Disketten-Icon) klicken → schreibt Running→Startup.
*(CLI-Äquivalent wäre `save`.)*
## Verifikation
- **VLAN → Membership**: Port `<PORT>` = U bei VLAN1, T bei 10/20/30/40/90/99/100.
- Switch-UI weiter erreichbar (https://10.40.41.239) → **kein Lockout**.
- WLAN **BF-Main** (vlan1) am hAP funktioniert unverändert (Client bekommt weiter FB-DHCP).
- Optional Gegencheck per CLI (Telnet, 1 VTY, sauber ausloggen): `show vlan`, `show running-config` → der Port zeigt `switchport ... vlan ...`.
## Sonderfall ap2 (Port 8 ist live!)
ap2 hängt hinter dem **TL-SG108E** am **Core-Port 8**, der **.10/.167/.247 trägt**. Optionen:
- **Jetzt nichts tun** — ap2 funkt „BF-Main" untagged auf vlan1 über den bestehenden Pfad (fürs Flach-WLAN-Testen reicht das).
- **Sauber (empfohlen vor Phase B):** ap2 in einem Wartungsfenster an einen **freien Core-Port** (z.B. Port 3) umstecken → diesen Port nach genau dieser Anleitung trunken. Dann muss der TL-SG108E nicht 802.1Q können.
- Trunk über Port 8 (TL-SG108E müsste taggen) = fummelig → **nicht empfohlen**.
## Offen / von dir bestätigen
- [ ] **ap1-Port** = wirklich Port **2**? (sonst `<PORT>` anpassen)
- [ ] **ap2-Verkabelung**: Port 8 lassen vs. an freien Core-Port umstecken?
- [ ] Jetzt schon Trunk vorbereiten (safe) oder erst zum Phase-B-Cutover?

View File

@@ -0,0 +1,94 @@
# ALLNET ALL-SG8316M (sw1) — Web-UI-Guide: Management-VLAN 1 → 99 (TP19.1)
> **Methode:** Claude erklärt Absicht + Klickpfad, **du klickst**. UI = Realtek-OEM (Backbone-SPA),
> FW 2.2.1.2956, erreichbar unter **https://10.40.41.11** (Login `claudecode`, PW in `switch/.secrets`).
> Die VLAN-Menü-Labels unten (`VLAN → Port Setting`, `VLAN → Membership`) sind aus einer echten Scrape
> auf **genau dieser Firmware** (`switch/ui-guides/01-trunk-ap-ports.md`, 2026-06-09) — sollten exakt passen.
> Die **System/IP/Management-VLAN-Labels sind NICHT live gescraped** (nur aus der alten FW1.0-CGI-Map
> abgeleitet, `switch/sg8316m-cgi-map.md`) — Seitentitel können abweichen, die **Absicht** steht bei
> jedem Schritt dabei, danach suchen.
## Was wir wollen (Absicht in einem Satz)
sw1s eigene Management-Adresse von `10.40.41.11` (VLAN1, interim) auf **`10.40.99.11/24`, GW `10.40.99.1`,
Management-VLAN `99`** umstellen — der letzte von drei TP19.1-Kandidaten (nach ap2, vor... eigentlich
**zuletzt**, s. Reihenfolge unten). VLAN 99 (MGMT) existiert bereits in der VLAN-DB (Name "MGMT" gesetzt),
und alle Trunk-Ports (GE1/GE2/GE8 + der ap1-Uplink) tragen VLAN 99 bereits **tagged** (TP1 ✅) — es muss
**keine VLAN- oder Port-Config** geändert werden, nur die switch-eigene Mgmt-Bindung.
## ⚠️ Bevor du klickst
- **Reihenfolge (aus `docs/ip-plan.md` Phase 2): ap2 zuerst, dann sw2, dann sw1 zuletzt** — sw1 ist das
risikoreichste Gerät (einziger Netzpfad zu sw2 hängt am sw1-Uplink; wird sw1 unerreichbar, ist auch
sw2 nicht mehr über den Uplink diagnostizierbar). Wenn ap2/sw2 noch nicht durch sind: die hier lieber
nicht vorziehen.
- **Telnet-CLI ist aktuell kaputt** (`claudecode`-Login wird deterministisch abgelehnt, s. Memory
`project_tp19_vlan1_status_20260716`) — d.h. **kein CLI-Fallback**, falls die Web-UI nach dem Schritt
hängt. Das Web-UI-Login selbst ist unabhängig davon (anderer Login-Pfad) — trotzdem als Schritt 0 zuerst
verifizieren, nicht blind loslegen.
- **Die eingebaute Sicherheitsnetz-Mechanik dieses Geräts: Änderungen wirken erst nach explizitem `save`
in Flash.** Solange du **nicht speicherst**, überlebt die Änderung keinen Reboot/Stromausfall — das ist
hier bewusst der Rollback-Pfad (s. unten). **Also: NICHT sofort speichern, erst verifizieren.**
- Kein bekannter Seriell-/Konsolen-Zugang an diesem Gerät. Physische Reset-Taste = **Werksreset** (komplette
Config weg, alle VLANs/Trunks/Namen neu von den `.cli`-Skripten aufbauen) — nur allerletzter Ausweg.
## CLI-Äquivalent (nur zum Gegenchecken/für später, falls Telnet wieder geht)
```
configure
ip address 10.40.99.11 mask 255.255.255.0
ip default-gateway 10.40.99.1
management-vlan vlan 99
end
# NICHT sofort:
save
```
(Quelle: `switch/cli/CLI-REFERENCE.md`.)
## Klickpfad
### 0) Login & Vorprüfung
1. Browser → **https://10.40.41.11** → Login `claudecode` (PW `switch/.secrets`).
2. **VLAN → Membership**: prüfen, dass **VLAN 99** existiert und auf dem Port, der zu ap1 führt
(laut `routeros/wan-cutover-runbook.md` = **gi15**; falls die UI andere Portnummern zeigt, den Port
nehmen, der die meisten VLANs tagged trägt — das ist der AP-Uplink), als **Tagged (T)** gelistet ist.
Falls nicht: **hier abbrechen**, nicht weitermachen (Physical-Path fehlt sonst).
### 1) IP-Adresse setzen — vermutlich `System → IP Setting` (Label ungescraped, s.o.)
1. Menü mit "IP"/"Network"/"System" suchen (nicht "VLAN").
2. Felder:
- **IP-Adresse** = `10.40.99.11`
- **Subnetzmaske** = `255.255.255.0`
- **Default Gateway** = `10.40.99.1`
- Modus: **Static** (nicht DHCP)
3. **Noch NICHT Apply/Save drücken, wenn die Seite auch das Management-VLAN-Feld enthält** — erst
Schritt 2 mit auf derselben Seite ausfüllen (viele Realtek-OEM-UIs haben IP + Mgmt-VLAN auf einer
gemeinsamen Seite). Falls getrennte Seiten: Apply hier ist ok, weiter zu Schritt 2.
### 2) Management-VLAN setzen — vermutlich gleiche Seite oder `System → Management VLAN`
1. Feld **Management VLAN / VLAN ID** = `99`.
2. **Jetzt Apply/Submit.**
3. **Ab hier ist deine aktuelle Browser-Session zur alten `.41.11`-Adresse tot** — das ist erwartet,
keine Störung. Der Switch beantwortet ab jetzt nur noch auf `10.40.99.11`.
### 3) Reconnect & Verifikation (BEVOR gespeichert wird!)
1. Neuer Tab → **https://10.40.99.11** aufrufen.
2. Erreichbar + Login geht? → weiter zu Schritt 4 (Save).
3. **Nicht erreichbar nach ~1 Minute?** → **nicht weiter versuchen, nicht neu konfigurieren** — stattdessen
Switch stromlos machen und neu starten (Power-Cycle). Da **nicht gespeichert wurde**, bootet er mit der
alten Config (`10.40.41.11`, VLAN1) wieder hoch → zurück auf Los, ohne Schaden. Danach mir Bescheid
geben, bevor der nächste Versuch gestartet wird (ggf. stimmt eine Portzuordnung nicht).
### 4) Speichern (erst wenn Schritt 3 erfolgreich war!)
- **System → Save Config** (oder Disketten-Symbol) → "Save to Flash". Ab jetzt übersteht die neue
Mgmt-Adresse einen Reboot.
## Verifikation (nach Save)
- `https://10.40.99.11` weiterhin erreichbar.
- `switch/.secrets` `SW_HOST` auf `10.40.99.11` aktualisieren (für zukünftige Telnet-Versuche, falls
der Login-Blocker sich klärt).
- ap2 (sofern schon auf VLAN99 umgezogen) und sw2 weiterhin über VLAN99 erreichbar — kein Kollateralschaden
am gemeinsamen MGMT-Segment.
- `docs/ip-plan.md` Zeile sw1: `bestätigt` = ✅ setzen. `CLAUDE.md`-Hardwaretabelle nachziehen.
## Nachziehen (nach erfolgreichem Cutover, alle drei TP19.1-Kandidaten durch)
- `docs/teilprojekte.md` TP19.1 auf ✅ setzen.
- Governance-Hinweis beachten: TP19.1 ist die **letzte Ausnahme** vom ap1/ap2-Config-Freeze bis TP21
(Nautobot) steht — danach keine weiteren ad-hoc-Router-Changes mehr ohne Rücksprache.