GitHub - bobberb/dgs-cli

8 min read Original article ↗

A CLI and TUI tool for managing D-Link DGS series switches (DGS-1100 now, DGS-1210 planned) — 56 commands, config version control, multi-switch support, and a Prometheus exporter, all driven through headless Chrome because D-Link gave us a JavaScript web UI and nothing else.

Why this exists

The DGS-1100 is a $40 managed gigabit switch that punches way above its price. 802.1Q VLANs, port trunking, IGMP snooping, STP — real managed switch features in a fanless desktop form factor. The catch: its only management interface is a web UI built entirely in JavaScript circa 2015, with no documented API, no SSH, and SNMP that's read-only for everything useful.

If you want to set up VLANs on one switch, the web UI is fine. If you want to configure a fleet of them, or version-control your switch configs, or just not mis-click a radio button and knock yourself offline, you need something scriptable.

There's nothing to reverse-engineer in the traditional sense — no firmware to unpack, no hidden REST endpoints. The switch serves a frameset with JavaScript that populates variables by fetching .js data files via XHR. VLAN membership is a string like 'U00T0TUU' where each character is a port. Configuration changes submit forms to CGI endpoints like VLANRename.cgi. It's all right there in the page source, just tedious to drive by hand.

So this tool drives headless Chrome via Selenium to do it for you. It logs in, navigates the frames, scrapes the JS data files for current state, and fills/submits forms for changes. Every write command reads back the config after applying to verify the switch actually accepted it. 56 commands covering every feature the switch exposes — VLANs, PVIDs, STP, IGMP, storm control, port security, mirroring, QoS, bandwidth limits, SNMP, traffic segmentation, voice/surveillance VLANs, and more.

What it does

Read your config in one shot instead of clicking through 6 different pages:

$ dgs-cli status

  IP: 10.1.1.250  MAC: 3C-33-32-63-01-A0
  Firmware: Ver 1.00.003

┌──────────────────────────────────────────────────────────────┐
│ 802.1Q VLAN Configuration                                   │
├──────┬──────────┬───────────────────────┬───────────────────┤
│ VID  │ Name     │ Untagged              │ Tagged            │
├──────┼──────────┼───────────────────────┼───────────────────┤
│ 3    │ IOT      │ eth8                  │ eth2,eth3         │
│ 4    │ GUEST    │                       │ eth2,eth3         │
│ 10   │ HOME     │ eth1,eth4,eth5,eth6,eth7 │ eth2,eth3         │
└──────┴──────────┴───────────────────────┴───────────────────┘

┌──────────────────────────────────────────┐
│ Features                                 │
├──────────────────────┬───────────────────┤
│ STP (RSTP)           │ ON                │
│ Loopback Detection   │ ON                │
│ IGMP Snooping        │ ON                │
│ Storm Control        │ ON                │
│ DDP                  │ off               │
└──────────────────────┴───────────────────┘

Interactive TUI with live-updating dashboard:

$ dgs-cli tui

┌─ Ports ──────────────────────┬─ Features ────────┐
│ Port Link Speed PVID TX  RX  │ STP (RSTP)    ON  │
│ eth1 UP   1000M 10   12M 3M  │ Loopback Det  ON  │
│ eth2 UP   1000M 10   7M  17M │ IGMP Snooping ON  │
│ eth3 UP   1000M 10   5M  4M  │ Storm Control ON  │
│ eth4 down —     10   391 164 │ DDP           off │
├─ VLANs ──────────────────────┼─ Issues ──────────┤
│ 3  IOT   eth8    eth2,eth3   │ SUGGESTIONS:      │
│ 4  GUEST         eth2,eth3   │ * VLAN 1 is empty │
│ 10 HOME  eth1..7 eth2,eth3   │                   │
└──────────────────────────────┴───────────────────┘
  r=Refresh  h=Harden  s=Save  q=Quit

Audit your config for 20 categories of issues — PVID mismatches, orphaned ports, incomplete trunks, disabled security features, error counters:

$ dgs-cli recommend
⚠  ISSUES FOUND:
   • Both STP and loopback detection are disabled — no loop protection!

💡 SUGGESTIONS:
   • VLAN 1 is empty — consider deleting it to reduce clutter
   • Storm control is disabled — broadcast storms will not be throttled

Auto-fix what recommend finds:

$ dgs-cli fix
Found 4 auto-fixable item(s):

  1. Enable STP (RSTP)
  2. Enable loopback detection
  3. Enable storm control
  4. Delete empty VLAN 1

Apply all 4 fixes? [y/N] y

>>> Enable STP (RSTP)
STP enabled (RSTP) [verified]
>>> Enable loopback detection
Loopback detection enabled [verified]
...

4/4 fixes applied — run 'save' to persist

Script changes without touching a browser:

$ dgs-cli vlan-add 20 CAMERAS U:6-7,T:2-3
VLAN 20 (CAMERAS) configured: untagged=[6, 7] tagged=[2, 3] [verified]

Batch multiple changes from a file (one login, one Chrome session):

$ cat setup.txt
# Add camera VLAN
vlan-add 20 CAMERAS U:6-7,T:2-3
pvid 6 20
pvid 7 20
harden
save

$ dgs-cli batch setup.txt

Version-control your switch config by dumping to JSON and diffing later:

$ dgs-cli dump > switch-config.json
$ git add switch-config.json && git commit -m "baseline config"
# ... time passes, someone changes something ...
$ dgs-cli diff switch-config.json
Changes from switch-config.json:

  ~ VLAN 10 membership: UTTUUUU0 -> UTTUUU00
  ~ eth7 PVID: 10 -> 1
  ~ storm_control: True -> False

3 difference(s) found

Generate reproducible configs from a dump:

$ dgs-cli template switch-config.json
vlan-add 3 IOT U:8,T:2,3
vlan-add 10 HOME U:1,4,5,6,7,T:2,3
pvid 1 10
...
stp on
storm on 1000
save

Manage multiple switches from one command:

$ dgs-cli --ip 10.1.1.250,10.1.2.250,10.1.3.250 recommend
$ dgs-cli --ip 10.1.1.250,10.1.2.250 backup-all ./backups
$ dgs-cli compare 10.1.1.250 10.1.2.250

Harden in one shot — enables STP, LBD, IGMP snooping, storm control, and disables DDP:

$ dgs-cli harden
STP enabled (RSTP) [verified]
Loopback detection enabled [verified]
IGMP snooping enabled [verified]
Storm control enabled (threshold: 1000 kbps) [verified]
D-Link Discovery Protocol disabled [verified]

Supported hardware

Model Status
DGS-1100 (08V2, 05V2, etc.) Supported — tested on 08V2
DGS-1210 Planned

Auto-detects firmware path within a model family, so other DGS-1100 variants (5-port, 12-port) should work without code changes — hasn't been verified on hardware yet.

Factory default access

Out of the box, the DGS-1100 lives at 10.90.90.90 on VLAN 1 with no password. To reach it, you need to be on the same subnet:

# Temporarily add an IP on the same /8 network to reach the switch
sudo ip addr add 10.90.90.91/8 dev eth0

# Verify connectivity
ping 10.90.90.90

# Run setup wizard to configure VLANs, IP, password, etc.
dgs-cli --ip 10.90.90.90 setup

# Remove the temporary IP after reconfiguring the switch
sudo ip addr del 10.90.90.91/8 dev eth0

Requirements

  • Python 3 with selenium
  • ChromeDriver + Chromium
  • Network access to the switch
  • textual (optional, for TUI mode)

With Nix flake (recommended):

nix run .# -- status                           # run directly
nix develop                                     # dev shell with all deps

With nix-shell:

nix-shell -p 'python3.withPackages(ps: with ps; [selenium])' chromedriver chromium

Configuration

Environment variables (defaults shown):

export SWITCH_MODEL=1100                         # 1100 (default) or 1210
export SWITCH_IP=10.1.1.250
export SWITCH_PASS=admin
export SWITCH_AUDIT_LOG=~/.switch-audit.log     # optional, enables audit logging

Or pass as flags:

dgs-cli --1100 --ip 192.168.1.1 --pass secret status
dgs-cli --1210 --ip 10.1.2.1 status

Usage

dgs-cli [--1100|--1210] [--ip IP] [--pass PASS] [--json] [command]

All commands

63 commands organized by category. Run help COMMAND for detailed usage.

Read (safe, no changes): status dump diff recommend fdb info pages read cable-diag export-csv

VLANs: vlan-add vlan-delete vlan-rename pvid pvid-all

Ports: port (enable/disable/speed/flow/describe) mirror qos bandwidth

Security: stp stp-port lbd igmp storm port-security ddp traffic-segm static-mac fdb-learning

Network: ip mgmt-vlan snmp snmp-community snmp-host voice-vlan surv-vlan jumbo eee

Operations: harden fix restore compare clone template backup-all batch watch tui prometheus webhook history

Admin: password sysname save reboot reset setup

Port spec format

Ports are specified as comma-separated values with optional ranges:

  • 1 — single port
  • 1,3,5 — multiple ports
  • 4-8 — range
  • 1,4-8 — mixed

VLAN port spec format

Prefix ports with U: (untagged) or T: (tagged):

  • U:1,4-8 — ports 1,4-8 untagged
  • T:2-3 — ports 2-3 tagged
  • U:1,4-8,T:2-3 — combined

Config analysis

recommend crawls 13 data files from the switch and checks for:

  • PVID/untagged VLAN mismatches
  • Ports not in any VLAN
  • Ports untagged in multiple VLANs
  • Trunk ports missing VLANs
  • Trunk group member consistency
  • Management VLAN disabled or nonexistent
  • Port admin state (disabled ports flagged)
  • TX/RX error counters
  • Zero-traffic ports
  • Flow control enabled (head-of-line blocking risk)
  • Empty VLAN 1
  • STP and loopback detection status
  • IGMP snooping disabled with multiple VLANs
  • Port mirroring active (performance impact)
  • Storm control disabled
  • Port security disabled on all ports
  • Bandwidth limits configured
  • DHCP-assigned switch IP
  • D-Link Discovery Protocol enabled

Shell completions

# Bash
eval "$(dgs-cli --completions bash)"

# Zsh
eval "$(dgs-cli --completions zsh)"

# Fish
dgs-cli --completions fish | source

Monitoring

Prometheus exporter — serves port stats, link state, and feature flags:

$ dgs-cli prometheus 9100
# http://localhost:9100/metrics
# switch_port_tx_ok{port="eth1"} 12432179
# switch_port_link{port="eth1"} 1
# switch_feature_enabled{feature="stp"} 1

Webhook — POST issues to Slack, Mattermost, or any URL:

$ dgs-cli webhook https://hooks.slack.com/services/...

Audit log

Set SWITCH_AUDIT_LOG to log every write command with timestamp and switch IP:

export SWITCH_AUDIT_LOG=~/.switch-audit.log
dgs-cli stp on
dgs-cli history
# 2026-03-23T19:30:00  10.1.1.250  stp on

Filter by switch or command:

dgs-cli history --ip 10.1.1.250 --cmd vlan-add

Tests

nix develop
pytest test_dgs_cli.py -v

142 tests, all offline (mock driver). Covers parsing, VLAN data extraction, health data parsing, and config analysis logic across all 20 check categories.