Ir al contenido

API de CGNAT y anti-spoofing

Dos funciones de protección de abonados del DTVSOL Super Router:

  • CGNAT asigna las direcciones privadas de los abonados a un pool de direcciones IPv4 públicas. Cada abonado recibe un slot fijo: una dirección pública y un bloque fijo de puertos en ella. Como la asignación es determinista, una dirección pública y un puerto siempre se pueden rastrear hasta un abonado (/cgnat/lookup). Las asignaciones también se escriben en un registro de auditoría en el router.
  • Anti-spoofing vincula la MAC de origen, la dirección IP y la VLAN de cada abonado. En cada VLAN de acceso con control aplicado, un paquete se reenvía solo cuando su MAC e IP de origen forman un par que el router conoce, y un paquete ARP solo cuando lo forman su MAC e IP de remitente. Todo lo demás se descarta y se registra (con límite de frecuencia) junto con la MAC que lo envió.

La autenticación, el formato de errores y los códigos de estado se describen en la descripción general de la API. Las mismas funciones están disponibles desde la CLI como dtvsol cgnat … y dtvsol antispoof ….

La configuración de CGNAT, su capacidad y una muestra de las asignaciones actuales (las cinco primeras).

Ventana de 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 es (port_max − port_min + 1) / block_size; capacity es pool_ips × subs_per_ip.

Modifique cualquier subconjunto de la configuración de CGNAT. Los campos que omita conservan su valor actual; los valores predeterminados son port_min 1024, port_max 65535, block_size 2048.

Nombre En Tipo Notas
enabled body boolean true/false (también 1/0, on/off, yes/no). Para habilitarlo se requiere una iface válida y existente y un pool no vacío.
iface body string La interfaz WAN en la que está el pool público, p. ej. bond0.
pool body string o array Direcciones IPv4 públicas: direcciones individuales y rangos first-last, separadas por comas o como array.
port_min body integer Primer puerto que se asigna (predeterminado 1024).
port_max body integer Último puerto que se asigna (predeterminado 65535).
block_size body integer Puertos por abonado (predeterminado 2048).
exempt body string o array Redes (a.b.c.d/nn) a las que nunca se aplica NAT, separadas por comas o como array.
Ventana de 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 es la respuesta completa de GET /cgnat. Errores: 400 — Enable requires a valid WAN iface (habilitar requiere una interfaz WAN válida), Enable requires a non-empty public IP pool (habilitar requiere un pool de IP públicas no vacío), Invalid exempt network: … (red exenta no válida).

Quién tenía una dirección pública y un puerto: el abonado cuyo slot los incluye. Úselo para responder a reclamos por abuso y a requerimientos de las autoridades.

Nombre En Tipo Notas
public_ip query string La dirección pública vista desde fuera. Obligatorio.
port query integer El puerto de origen público visto desde fuera.
Ventana de 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
}

Cuando ningún slot coincide: {"ok": true, "code": 200, "found": false, "public_ip": "XXX.XXX.XXX.10", "port": 2000}. Una public_ip no válida da 400 Invalid public_ip. La consulta refleja las asignaciones actuales; para momentos pasados use el registro de auditoría.

Modos:

Modo Comportamiento
strict Solo pasan los clientes registrados, fijados a su dirección. Los dispositivos no registrados siguen recibiendo DHCP (para que aparezcan en las listas de IP) pero nada más. Es el predeterminado.
dynamic Los clientes registrados quedan fijados a su dirección, y a los dispositivos no registrados se les permite la dirección que les concedió el servidor DHCP.
off Solo por VLAN: excluye esa VLAN.
default Solo por VLAN: quita la excepción para que la VLAN siga el modo global.

Cuando está habilitado, en cada VLAN que sirve el servidor DHCP se aplica el control en el modo global; una excepción por VLAN puede cambiar el modo, desactivar una VLAN o agregar una VLAN que el servidor DHCP no sirve (segmentos solo estáticos). Para IPv6, la dirección reservada de un cliente, su dirección DHCPv6 y su prefijo delegado se vinculan a su MAC; las direcciones link-local siempre pasan; un Router Advertisement o un Redirect enviado por un abonado se descarta. La suplantación entre abonados dentro de la misma VLAN nunca llega al router y debe detenerse con el split-horizon de la OLT.

Qué se está aplicando: configuración global, modo por VLAN, cantidad de vinculaciones, contadores de descartes y el resumen de descartes de la última hora.

Ventana de 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:"]
}

Envíe al menos uno de los campos; los demás conservan su valor. Deshabilitarlo elimina todas las reglas pero conserva la configuración.

Nombre En Tipo Notas
enabled body boolean true/false (también 1/0, on/off, yes/no); cualquier otro valor da 400.
mode body string strict o dynamic.
log body boolean Registro en el log del kernel, con límite de frecuencia, de lo que se descartó.
exempt body string o array Redes (CIDR) permitidas desde cualquier MAC en cada VLAN con control aplicado — por ejemplo, la dirección de relay o de gestión de una OLT. Separadas por comas o como array; un valor vacío borra la lista.
Ventana de 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 es uno de Anti-spoofing enabled (habilitado), Anti-spoofing updated (actualizado), Anti-spoofing disabled — rules removed (deshabilitado, reglas eliminadas), Anti-spoofing is off (está desactivado). status es la respuesta completa de GET /antispoof. Errores: 400 si no hay nada que establecer, un booleano incorrecto, un modo incorrecto o una red exenta no válida; 500 cuando no se pudieron aplicar las reglas (ok: false, con detalles en apply.errors).

Nombre En Tipo Notas
iface path string Nombre de la interfaz, p. ej. vlan100 (hasta 15 caracteres). Debe existir o ser servida por DHCP.
mode body string strict, dynamic, off o default.
Ventana de 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 }
}

Cuando el anti-spoofing está desactivado globalmente, el modo se registra, apply es {"applied": false, "action": "feature off"} y note indica que entrará en vigor cuando se active el anti-spoofing. Errores: 400 nombre de interfaz o modo no válido; 404 la interfaz no existe y no es servida por DHCP.

Igual que POST /antispoof/{iface} con mode default.

Ventana de 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 } }

Los paquetes descartados según el log del kernel, del más reciente al más antiguo, y los dispositivos que los causaron agrupados por VLAN + MAC + dirección de origen (primero los que tienen más descartes).

Nombre En Tipo Notas
since query string 30m, 2h, 1d, 45s, o un momento como 2026-09-17 10:00. Predeterminado: una hora.
limit query integer Cuántas entradas devolver, 1–500 (predeterminado 50). total siempre cuenta todas.
Ventana de 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" }
]
}

Las entradas IP incluyen proto (los tipos ICMPv6 con nombre, p. ej. ICMPv6/RA) y dport; las entradas ARP incluyen op (request o reply). Un since incorrecto da 400.

Normalmente no es necesario: cada cambio de cliente o de VLAN, y cada cambio de concesión DHCP, reconstruye las reglas automáticamente.

Ventana de 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 }

Cuando el anti-spoofing está desactivado: "message": "Anti-spoofing is off; nothing to apply" (está desactivado; no hay nada que aplicar). 500 con ok: false cuando no se pudieron aplicar las reglas.

Las mismas operaciones mediante GET /api?action=…, con todos los parámetros en la cadena de consulta (vea la API de acciones).

Acción 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 sitio fue escrito con ayuda de IA y revisado por nuestro equipo.