Initial commit: BF Core-Switch-Prep + VLAN-Architektur + Delegations-Übergabe

- ALL-SG8316M auf FW 2.2.1.2956 geflasht; VLAN-DB 10/20/30/40/100 via Telnet-CLI (non-disruptiv)
- CLI-Tooling: run-cli.exp + .cli-Files + CLI-REFERENCE (FW 2.2.1, IOS-artig)
- Kanonisches Inventar: bf.csv/md + build.py (Doku-Motor), Core-Port-Map
- Finaler VLAN-/Adressplan (10.40.<vlanid>.0/24), Firmware-Runbooks, Recovery-Assets
- Secrets/Firmware/State/DS_Store via .gitignore ausgeschlossen

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Christoph Emsenhuber
2026-06-09 18:08:45 +02:00
commit 498fa06472
25 changed files with 1474 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

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,47 @@
# 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>
ip default-gateway <A.B.C.D>
```
- ⚠️ **Netzmasken-Syntax noch zu bestätigen**: `ip address ?` zeigte nur `A.B.C.D`. Vor dem Commit
`ip address <ip> ?` prüfen ODER `show running-config` ansehen (wie die bestehende Zeile geschrieben ist) und spiegeln.
- 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:** Syntax noch zu erkunden (config-vlan `name`? `vlan <id>` einzeln?) — beim nächsten Lauf via `?` prüfen.
## 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`.

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