Protection API
Endpoints that protect and expose the DTVSOL Super Router itself: who may reach the management ports, who fail2ban has banned, which public ports are forwarded to subscribers, the per-MAC forwarding rules, DHCP option 82 (relay circuit / remote ID) and the router’s own SNMP agent.
Authentication, error format and status codes are described in the API overview.
Management allow-list
Section titled “Management allow-list”The API port (8880 by default) and the OLT monitor port (8881 by default) sit behind one allow-list. While the list is empty both ports are open to any source ("status": "open (no restrictions)"); as soon as it holds one network, only new connections from the listed networks are accepted on those ports and every other new connection is dropped. The rules are saved so they survive a reboot.
GET /protect
Section titled “GET /protect”The allowed networks.
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/protect{ "port": 8880, "count": 1, "networks": [ { "network": "XXX.XXX.XXX.0/24", "comment": "NOC", "added": "2026-09-27 10:00:00" } ], "status": "protected"}POST /protect
Section titled “POST /protect”| Name | In | Type | Notes |
|---|---|---|---|
network |
body | string | An IPv4 address or address/0..32. A bare address is stored as /32. Required. |
comment |
body | string | Free text. |
curl -X POST http://ROUTER-IP:8880/protect \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"network": "XXX.XXX.XXX.0/24", "comment": "NOC"}'{ "ok": true, "code": 201, "message": "Network XXX.XXX.XXX.0/24 allowed", "networks": [ { "network": "XXX.XXX.XXX.0/24", "comment": "NOC", "added": "2026-09-27 10:00:00" } ]}Errors: 400 Invalid network: …, 409 Network … already allowed. GET /protect/add?network=…&comment=… does the same with query parameters.
DELETE /protect
Section titled “DELETE /protect”| Name | In | Type | Notes |
|---|---|---|---|
network |
body | string | As it was added; a bare address means /32. |
curl -X DELETE http://ROUTER-IP:8880/protect \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"network": "XXX.XXX.XXX.0/24"}'{ "ok": true, "code": 200, "message": "Network XXX.XXX.XXX.0/24 removed", "networks": [] }404 Network … not found when it is not on the list. GET /protect/delete?network=… does the same with query parameters.
fail2ban
Section titled “fail2ban”fail2ban bans sources that repeatedly fail authentication (the API logs every rejected key).
GET /fail2ban
Section titled “GET /fail2ban”Whether fail2ban runs, and each jail’s counters and banned addresses.
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/fail2ban{ "running": true, "jails": [ { "jail": "sshd", "currently_banned": 2, "total_banned": 7, "banned_ips": ["XXX.XXX.XXX.1", "XXX.XXX.XXX.7"] } ]}When fail2ban is not active: {"running": false, "jails": []}.
POST /fail2ban
Section titled “POST /fail2ban”| Name | In | Type | Notes |
|---|---|---|---|
unban |
body | string | The IPv4 or IPv6 address to unban. |
curl -X POST http://ROUTER-IP:8880/fail2ban \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"unban": "XXX.XXX.XXX.1"}'{ "ok": true, "code": 200, "message": "Unbanned XXX.XXX.XXX.1", "detail": "" }400 Invalid IP; 500 Unban failed (with detail) when fail2ban refuses.
Port forwarding
Section titled “Port forwarding”Inbound port forwards (DNAT) from a public address and port to a subscriber’s address and port. /portforward is an alias of /pf with exactly the same behaviour. A forward is identified by protocol + public address + public port. Forwards whose client address sits on a switched-off VLAN are kept but not applied until the VLAN is switched on again.
GET /pf
Section titled “GET /pf”The saved forwards and the live NAT rules that carry them.
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/pf{ "count": 1, "forwards": [ { "proto": "tcp", "public_ip": "XXX.XXX.XXX.10", "public_port": 8080, "client_ip": "100.64.0.2", "client_port": 80, "comment": "camera", "created": "2026-09-27 10:00:00" } ], "live": ["-A PREROUTING -d XXX.XXX.XXX.10/32 -p tcp -m tcp --dport 8080 … -j DNAT --to-destination 100.64.0.2:80"]}GET /portforward
Section titled “GET /portforward”Alias of GET /pf.
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/portforwardPOST /pf
Section titled “POST /pf”| Name | In | Type | Notes |
|---|---|---|---|
proto |
body | string | tcp (default) or udp. |
public_ip |
body | string | Public IPv4 address; empty means any address on the router. |
public_port |
body | integer | 1–65535. Required. |
client_ip |
body | string | The subscriber’s IPv4 address. Required. |
client_port |
body | integer | 1–65535. Required. |
comment |
body | string | Free text. |
curl -X POST http://ROUTER-IP:8880/pf \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"proto": "tcp", "public_ip": "XXX.XXX.XXX.10", "public_port": 8080, "client_ip": "100.64.0.2", "client_port": 80, "comment": "camera"}'{ "ok": true, "code": 201, "message": "Port forward added", "forward": { "proto": "tcp", "public_ip": "XXX.XXX.XXX.10", "public_port": 8080, "client_ip": "100.64.0.2", "client_port": 80, "comment": "camera", "created": "2026-09-27 10:00:00" }}Errors: 400 (proto must be tcp or udp, Invalid public_ip, Ports must be 1..65535, Invalid client_ip); 409 A forward for that proto/public_ip:port already exists.
DELETE /pf
Section titled “DELETE /pf”| Name | In | Type | Notes |
|---|---|---|---|
proto |
body | string | tcp (default) or udp. |
public_ip |
body | string | As added; empty for “any address”. |
public_port |
body | integer | As added. |
curl -X DELETE http://ROUTER-IP:8880/pf \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"proto": "tcp", "public_ip": "XXX.XXX.XXX.10", "public_port": 8080}'{ "ok": true, "code": 200, "message": "Port forward removed" }404 No forward matching tcp/XXX.XXX.XXX.10:8080 when none matches.
Firewall view
Section titled “Firewall view”Read-only views of the per-MAC forwarding rules the router keeps for registered clients.
GET /firewall
Section titled “GET /firewall”The MAC rules in the forwarding chain, each with the client it belongs to.
| Name | In | Type | Notes |
|---|---|---|---|
network |
query | string | Only clients whose address is in this IPv4 network (a.b.c.d/nn); rules without a known client are then left out. |
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/firewall?network=100.64.0.0/22"{ "count": 1, "mac_rules": [ { "mac": "AA:BB:CC:DD:EE:FF", "iface": "vlan100", "client": { "ip": "100.64.0.2", "hostname": "client-aabbccddeeff", "comment": "" } } ]}Without a filter, a rule whose MAC is not a registered client has "client": null.
GET /firewall/full
Section titled “GET /firewall/full”The whole forwarding chain as the kernel lists it (verbose, with counters and line numbers), one string per line.
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/firewall/full{ "forward_chain": ["Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)", "num pkts bytes target prot opt in out source destination", "…"] }DHCP option 82
Section titled “DHCP option 82”When DHCP requests arrive through a relay (for example an OLT) that adds option 82, the relay’s circuit ID and remote ID identify the subscriber’s port.
GET /option82
Section titled “GET /option82”Leases that carry option 82 data, whether capture to the log is enabled, and the last captured log lines (up to 50).
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/option82{ "count": 1, "from_leases": [ { "ip": "100.64.0.2", "circuit_id": "0:1:2", "remote_id": "\"olt-1\"", "mac": "AA:BB:CC:DD:EE:FF" } ], "capture_enabled": true, "recent_log": ["… DTVSOL-OPT82 ip=100.64.0.2 circuit=00:01:02 remote=…"]}POST /option82
Section titled “POST /option82”| Name | In | Type | Notes |
|---|---|---|---|
enable |
body | boolean | true to log option 82 on every lease commit, false to stop. |
curl -X POST http://ROUTER-IP:8880/option82 \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"enable": true}'{ "ok": true, "code": 200, "message": "Option 82 capture enabled (logged to journal + parsed by /option82)" }If the DHCP server rejects the configuration, the change is reverted and the answer is 400 with detail; reading option 82 from the leases still works. Disabling answers "message": "Option 82 capture disabled".
SNMP agent
Section titled “SNMP agent”The router’s own read-only SNMP agent (v2c, UDP 161, IPv4 and IPv6), used by external monitoring systems to graph interface and per-client traffic.
GET /snmp
Section titled “GET /snmp”Whether the agent runs and its settings. The community itself is never returned — only whether one is set.
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/snmp{ "running": true, "enabled": "enabled", "listen": "udp/161 (IPv4+IPv6)", "community_set": true, "sys_location": "POP 1", "sys_contact": "noc@example.net", "per_client_count": 120, "sample": [ "…" ]}The answer also contains a few informational text fields describing what the agent publishes.
POST /snmp
Section titled “POST /snmp”Fields left out keep their current value.
| Name | In | Type | Notes |
|---|---|---|---|
community |
body | string | Read-only community: 1–64 characters of A-Z a-z 0-9 _ . : -. |
location |
body | string | System location text. |
contact |
body | string | System contact text. |
curl -X POST http://ROUTER-IP:8880/snmp \ -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \ -d '{"community": "YOUR_COMMUNITY", "location": "POP 1", "contact": "noc@example.net"}'{ "ok": true, "code": 200, "message": "SNMP configured", "detail": null }Errors: 400 Invalid community (A-Z a-z 0-9 _.:- , max 64); 500 when the settings could not be saved or the agent failed to restart (snmpd restart failed, with detail).
Action API equivalents
Section titled “Action API equivalents”The same operations through GET /api?action=…, with every parameter in the query string (see the action API).
| Action | Query parameters | Same as |
|---|---|---|
action=protect-list |
— | GET /protect |
action=protect-add |
network, comment |
POST /protect |
action=protect-delete |
network |
DELETE /protect |
action=fail2ban-status |
— | GET /fail2ban |
action=fail2ban-unban |
ip |
POST /fail2ban |
action=pf-list |
— | GET /pf |
action=pf-add |
proto, public_ip, public_port, client_ip, client_port, comment |
POST /pf |
action=pf-del |
proto, public_ip, public_port |
DELETE /pf |
action=firewall |
network |
GET /firewall |
action=firewall-full |
— | GET /firewall/full |
action=option82 |
— | GET /option82 |
action=snmp-status |
— | GET /snmp |
action=snmp-set |
community, location, contact |
POST /snmp |
This site was written with the help of AI and checked by our team.