Pular para o conteúdo

API de CGNAT e anti-spoofing

Dois recursos de proteção de assinantes do DTVSOL Super Router:

  • O CGNAT mapeia os endereços privados dos assinantes para um pool de endereços IPv4 públicos. Cada assinante recebe um slot fixo: um endereço público e um bloco fixo de portas nele. Como o mapeamento é determinístico, um endereço público e uma porta sempre podem ser rastreados até um único assinante (/cgnat/lookup). As atribuições também são gravadas em um log de auditoria no roteador.
  • O anti-spoofing vincula o MAC de origem, o endereço IP e a VLAN de cada assinante. Em toda VLAN de acesso com a proteção aplicada, um pacote só é encaminhado quando o seu MAC e IP de origem formam um par que o roteador conhece, e um pacote ARP só quando o MAC e IP do remetente formam esse par. Todo o resto é descartado e registrado (com limite de taxa) com o MAC que o enviou.

Autenticação, formato de erro e códigos de status estão descritos na visão geral da API. Os mesmos recursos estão disponíveis na CLI como dtvsol cgnat … e dtvsol antispoof ….

A configuração do CGNAT, sua capacidade e uma amostra das atribuições atuais (as cinco primeiras).

Janela do terminal
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/cgnat
{
"enabled": true,
"iface": "bond0",
"pool": "XXX.XXX.XXX.10-XXX.XXX.XXX.20",
"pool_ips": 11,
"port_range": "1024-65535",
"block_size": 2048,
"subs_per_ip": 31,
"capacity": 341,
"assigned": 2,
"free": 339,
"exempt": ["10.0.0.0/30"],
"sample": [
{ "mac": "AA:BB:CC:DD:EE:FF", "private_ip": "100.64.0.2", "public": "XXX.XXX.XXX.10:1024-3071", "slot": 0 }
],
"audit_log": "/opt/dtvsol/log/cgnat-mappings.log"
}

subs_per_ip é (port_max − port_min + 1) / block_size; capacity é pool_ips × subs_per_ip.

Altere qualquer subconjunto das configurações do CGNAT. Os campos que você omitir mantêm o valor atual; os padrões são port_min 1024, port_max 65535, block_size 2048.

Nome Em Tipo Observações
enabled body boolean true/false (também 1/0, on/off, yes/no). Para ativar é preciso uma iface válida e existente e um pool não vazio.
iface body string A interface WAN onde fica o pool público, por exemplo bond0.
pool body string or array Endereços IPv4 públicos: endereços avulsos e faixas first-last, separados por vírgula ou como array.
port_min body integer Primeira porta distribuída (padrão 1024).
port_max body integer Última porta distribuída (padrão 65535).
block_size body integer Portas por assinante (padrão 2048).
exempt body string or array Redes (a.b.c.d/nn) que nunca passam por NAT, separadas por vírgula ou como array.
Janela do terminal
curl -X POST http://ROUTER-IP:8880/cgnat \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled": true, "iface": "bond0", "pool": "XXX.XXX.XXX.10-XXX.XXX.XXX.20", "block_size": 2048, "exempt": "10.0.0.0/30"}'
{
"ok": true,
"code": 200,
"message": "CGNAT updated",
"status": { "enabled": true, "iface": "bond0", "capacity": 341, "assigned": 2, "free": 339 }
}

status é a resposta completa de GET /cgnat. Erros: 400 — Enable requires a valid WAN iface, Enable requires a non-empty public IP pool, Invalid exempt network: ….

Quem usava um endereço público e uma porta: o assinante cujo slot os cobre. Use para responder a denúncias de abuso e a solicitações de autoridades.

Nome Em Tipo Observações
public_ip query string O endereço público visto de fora. Obrigatório.
port query integer A porta de origem pública vista de fora.
Janela do terminal
curl -H "X-API-Key: YOUR_API_KEY" \
"http://ROUTER-IP:8880/cgnat/lookup?public_ip=XXX.XXX.XXX.10&port=2000"
{
"ok": true,
"code": 200,
"found": true,
"mac": "AA:BB:CC:DD:EE:FF",
"private_ip": "100.64.0.2",
"hostname": "client-aabbccddeeff",
"public_ip": "XXX.XXX.XXX.10",
"port_start": 1024,
"port_end": 3071,
"slot": 0
}

Quando nenhum slot corresponde: {"ok": true, "code": 200, "found": false, "public_ip": "XXX.XXX.XXX.10", "port": 2000}. Um public_ip inválido dá 400 Invalid public_ip. A consulta reflete as atribuições atuais; para momentos passados, use o log de auditoria.

Modos:

Modo Comportamento
strict Só os clientes cadastrados passam, presos ao seu endereço. Dispositivos não cadastrados ainda recebem DHCP (e por isso aparecem nas listas de IP), mas nada além disso. É o padrão.
dynamic Os clientes cadastrados ficam presos ao seu endereço, e dispositivos não cadastrados são permitidos no endereço que o servidor DHCP lhes concedeu.
off Somente por VLAN: exclui essa VLAN.
default Somente por VLAN: remove a exceção, e a VLAN volta a seguir o modo global.

Quando ativado, toda VLAN atendida pelo servidor DHCP passa a ter a proteção aplicada no modo global; uma exceção por VLAN pode mudar o modo, desligar uma VLAN ou incluir uma VLAN que o servidor DHCP não atende (segmentos só com endereços estáticos). Para IPv6, o endereço reservado do cliente, o seu endereço DHCPv6 e o seu prefixo delegado ficam vinculados ao seu MAC; link-local sempre passa; um Router Advertisement ou Redirect vindo de um assinante é descartado. O spoofing entre assinantes dentro da mesma VLAN nunca chega ao roteador e precisa ser barrado pelo split-horizon da OLT.

O que está sendo aplicado: configurações globais, modo por VLAN, contagem de vinculações, contadores de descarte e o resumo de descartes da última hora.

Janela do terminal
curl -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/antispoof
{
"enabled": true,
"mode": "strict",
"log": true,
"exempt": ["10.0.0.0/30"],
"overrides": { "vlan200": "dynamic" },
"served": ["vlan100", "vlan200"],
"enforced": {
"vlan100": {
"mode": "strict",
"exists": true,
"bindings4": 120,
"bindings6": 118,
"dropped": { "ip4": 42, "ip6": 3, "arp": 7 },
"clients": 120,
"service": false,
"leases": 0
}
},
"switched_off": [],
"live": { "v4_chain": true, "v4_rules": 240, "v4_forward": true, "v4_input": true, "v6_chain": true, "v6_rules": 236, "v6_forward": true, "v6_input": true },
"last_hour": { "attempts": 5, "devices": 1 },
"log_prefixes": ["DTVSOL_SPOOF:", "DTVSOL_ARPSPOOF:"]
}

Envie pelo menos um dos campos; os outros mantêm o valor. Desativar remove todas as regras, mas mantém as configurações.

Nome Em Tipo Observações
enabled body boolean true/false (também 1/0, on/off, yes/no); qualquer outro valor dá 400.
mode body string strict ou dynamic.
log body boolean Log do kernel, com limite de taxa, do que foi descartado.
exempt body string or array Redes (CIDR) permitidas a partir de qualquer MAC em todas as VLANs com a proteção aplicada — por exemplo o endereço de relay ou de gerência de uma OLT. Separadas por vírgula ou um array; um valor vazio limpa a lista.
Janela do terminal
curl -X POST http://ROUTER-IP:8880/antispoof \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"enabled": true, "mode": "strict", "exempt": "10.0.0.0/30"}'
{
"ok": true,
"code": 200,
"message": "Anti-spoofing enabled",
"apply": { "applied": true, "enabled": true, "bindings4": 120, "bindings6": 118 },
"status": { "enabled": true, "mode": "strict" }
}

message é um de Anti-spoofing enabled, Anti-spoofing updated, Anti-spoofing disabled — rules removed, Anti-spoofing is off. status é a resposta completa de GET /antispoof. Erros: 400 quando não há nada para definir, para um booleano inválido, um modo inválido ou uma rede de exceção inválida; 500 quando as regras não puderam ser aplicadas (ok: false, detalhes em apply.errors).

Nome Em Tipo Observações
iface path string Nome da interface, por exemplo vlan100 (até 15 caracteres). Precisa existir ou ser atendida por DHCP.
mode body string strict, dynamic, off ou default.
Janela do terminal
curl -X POST http://ROUTER-IP:8880/antispoof/vlan100 \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"mode": "dynamic"}'
{
"ok": true,
"code": 200,
"message": "vlan100 set to dynamic",
"iface": "vlan100",
"requested": "dynamic",
"effective": "dynamic",
"note": null,
"apply": { "applied": true }
}

Quando o anti-spoofing está desligado globalmente, o modo é gravado, apply é {"applied": false, "action": "feature off"} e note diz que ele entra em vigor quando o anti-spoofing for ligado. Erros: 400 nome de interface ou modo inválido; 404 a interface não existe e não é atendida por DHCP.

O mesmo que POST /antispoof/{iface} com mode default.

Janela do terminal
curl -X DELETE -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/antispoof/vlan100
{ "ok": true, "code": 200, "message": "vlan100 follows the default mode again", "iface": "vlan100", "requested": "default", "effective": "strict", "note": null, "apply": { "applied": true } }

Pacotes descartados, lidos do log do kernel, os mais recentes primeiro, e os dispositivos que os causaram, agrupados por VLAN + MAC + endereço de origem (os com mais descartes primeiro).

Nome Em Tipo Observações
since query string 30m, 2h, 1d, 45s, ou um horário como 2026-09-17 10:00. Padrão: uma hora.
limit query integer Quantas entradas retornar, 1–500 (padrão 50). total sempre conta todas.
Janela do terminal
curl -H "X-API-Key: YOUR_API_KEY" "http://ROUTER-IP:8880/antispoof/log?since=2h&limit=20"
{
"ok": true,
"code": 200,
"enabled": true,
"since": "2 hours ago",
"total": 3,
"offenders": [
{ "iface": "vlan100", "mac": "AA:BB:CC:DD:EE:FF", "src": "100.64.0.9", "count": 3, "last": "2026-09-27T10:00:02+0000", "ip": 2, "arp": 1 }
],
"entries": [
{ "time": "2026-09-27T10:00:02+0000", "kind": "ip", "iface": "vlan100", "mac": "AA:BB:CC:DD:EE:FF", "src": "100.64.0.9", "dst": "100.64.0.1", "proto": "UDP", "dport": 53 },
{ "time": "2026-09-27T10:00:01+0000", "kind": "arp", "iface": "vlan100", "mac": "AA:BB:CC:DD:EE:FF", "src": "100.64.0.9", "dst": "100.64.0.1", "op": "reply" }
]
}

As entradas de IP trazem proto (tipos ICMPv6 com nome, por exemplo ICMPv6/RA) e dport; as entradas de ARP trazem op (request ou reply). Um since inválido dá 400.

Normalmente não é necessário: toda alteração de cliente ou de VLAN, e toda mudança de concessão DHCP, reconstrói as regras automaticamente.

Janela do terminal
curl -X POST -H "X-API-Key: YOUR_API_KEY" http://ROUTER-IP:8880/antispoof/sync
{ "ok": true, "code": 200, "message": "Anti-spoofing rules rebuilt", "applied": true, "enabled": true }

Quando o anti-spoofing está desligado: "message": "Anti-spoofing is off; nothing to apply". 500 com ok: false quando as regras não puderam ser aplicadas.

As mesmas operações por GET /api?action=…, com todos os parâmetros na query string (veja a API de ações).

Ação Parâmetros de consulta Equivale a
action=cgnat-status — GET /cgnat
action=cgnat-set enabled, iface, pool, port_min, port_max, block_size, exempt POST /cgnat
action=cgnat-lookup public_ip, port GET /cgnat/lookup
action=antispoof-status — GET /antispoof
action=antispoof-set enabled, mode, log, exempt POST /antispoof
action=antispoof-iface iface, mode (strict, dynamic, off, default) POST /antispoof/{iface}
action=antispoof-log since, limit GET /antispoof/log
action=antispoof-sync — POST /antispoof/sync

Este site foi escrito com a ajuda de IA e revisado pela nossa equipe.