Starlink Mini 2 tras CGNAT: cómo llegar a un sitio que se mueve

Starlink Mini 2 tras CGNAT: cómo llegar a un sitio que se mueve

Starlink Mini 2 tras CGNAT: cómo llegar a un sitio que se mueve

Respuesta corta (comprobado el 2026-11-11): SpaceX ha abierto reservas por invitación de la Starlink Mini 2, una antena portátil con batería extraíble. Conectará más cámaras, sensores, portátiles de campo y oficinas temporales donde no llega la fibra. Lo que un plan estándar no les dará es una dirección accesible desde internet: el IPv4 de Starlink funciona por defecto con NAT de operador (CGNAT) y las conexiones entrantes están bloqueadas. Contamos el espacio IPv4 que anuncia la red de Starlink (990.208 direcciones para más de 12 millones de clientes), escribimos un script que indica si un host está tras CGNAT y comparamos cuatro formas de llegar a un sitio que se mueve.

Fuentes. La Mini 2 según Notebookcheck (Daniel Zlatev, 2026-11-10) y DishyTech (Colby Baber, 2026-11-09), debatida en Hacker News con 369 puntos y 380 comentarios a 2026-11-11. Política de direcciones: artículos del Starlink Help Center "What IP address does Starlink provide?" e "IP Address", leídos el 2026-11-11. Datos de enrutamiento: RIPEstat, consultado por nuestro script el 2026-11-11.

Qué trae de nuevo la Mini 2

Elemento Mini 2, según lo publicado el 2026-11-10
Batería Extraíble, 71 Wh, hasta 4,5 h (cifra de SpaceX), carga USB-C a 60 W
Wi-Fi Wi-Fi 6 (la primera Mini tenía Wi-Fi 5)
Construcción IP67, 314 x 263 x 23,1 mm, 1,6 kg con batería y soporte
Disponibilidad Reserva por invitación para algunos clientes de EE. UU., más países después
Precio Sin fijar: kit de $249 y batería de $50 en el código del sitio de Starlink (DishyTech), y una cifra de $999 en otras fuentes sin desglose

No se han publicado cifras de velocidad. Para este artículo lo importante es la batería: una antena que funciona cuatro horas sin red eléctrica es una antena que se lleva de un sitio a otro, y eso cambia cómo se llega a lo que hay detrás.

El problema de las direcciones: CGNAT por defecto

Las páginas de ayuda de Starlink describen dos políticas IPv4. La predeterminada es CGNAT: el router recibe una dirección de 100.64.0.0/10, el rango compartido del RFC 6598, muchos clientes comparten cada dirección pública y no se permite tráfico IPv4 entrante. Una dirección IPv4 pública es una opción solo en los planes Priority (las dos páginas de ayuda los nombran de forma distinta: Local y Global Priority en una, Priority, Mobile Priority y Maritime en la otra). Cada línea recibe además un prefijo IPv6 /56 mediante DHCPv6-PD, y Starlink dice que el IPv6 nativo funciona en todos sus routers, kits y planes.

Para una antena portátil importan dos frases más. Los routers Wi-Fi propios de Starlink no admiten reenvío de puertos ni reglas de firewall, ni para IPv4 ni para IPv6. Y la dirección IPv4 reservada y el prefijo IPv6 sobreviven a un reinicio, pero pueden cambiar si la antena se traslada o se actualiza su software. Una antena que se mueve es una dirección que se mueve.

Por qué no hay dirección para todos: nuestro recuento

Nuestro script preguntó a RIPEstat qué anuncia AS14593 (SPACEX-STARLINK): 2.274 prefijos IPv4 que cubren 990.208 direcciones únicas, la mayoría /23 y /24. Starlink dijo en junio de 2026 que tenía más de 12 millones de clientes activos (Mezha). Eso es más o menos una dirección IPv4 pública por cada 12 clientes. A la mediana de 90 días de ip4.market para un /24, $25,25 por dirección (índice actualizado el 2026-11-02, ver precios IPv4), una dirección por cliente costaría unos $303 millones; el espacio que anuncia AS14593 vale unos $25 millones a ese precio. En IPv6 es al revés: 1.130 prefijos, 668 de ellos /40, y un /40 contiene 65.536 /56 de cliente, así que solo esos /40 contienen unos 43,8 millones.

Límites del recuento: RIPEstat excluye las rutas que ven menos de 10 peers de RIS, Starlink puede llevar parte del tráfico con otros números de AS, y el espacio anunciado no es el espacio en uso. Aun así, esa proporción explica por qué CGNAT es lo predeterminado.

Script: ¿está este host tras CGNAT?

cgnat_check.js lista las direcciones del host y su tipo (pública, RFC 1918, 100.64.0.0/10), pregunta a un punto público qué dirección ve, busca la red que la anuncia y dice si el IPv4 entrante puede funcionar. Con un número de AS como argumento, además suma el espacio que anuncia esa red, fusionando prefijos solapados.

// cgnat_check.js - am I behind CGNAT, and can anything reach me from outside?
// Usage: bun cgnat_check.js [ASN]    (Bun or Node 18+, no dependencies)
import os from 'node:os'

const v4n = (ip) => ip.split('.').reduce((a, o) => a * 256 + Number(o), 0)
const inV4 = (ip, cidr) => {
  const [net, bits] = cidr.split('/')
  const size = 2 ** (32 - Number(bits))
  return Math.floor(v4n(ip) / size) === Math.floor(v4n(net) / size)
}
const kindV4 = (ip) =>
  inV4(ip, '100.64.0.0/10') ? 'CGNAT (RFC 6598)'
  : ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'].some((c) => inV4(ip, c)) ? 'private (RFC 1918)'
  : inV4(ip, '169.254.0.0/16') ? 'link-local'
  : inV4(ip, '127.0.0.0/8') ? 'loopback' : 'public'
const kindV6 = (ip) => {
  const h = parseInt(ip.split(':')[0] || '0', 16)
  return (h & 0xe000) === 0x2000 ? 'global' : (h & 0xfe00) === 0xfc00 ? 'ULA' : (h & 0xffc0) === 0xfe80 ? 'link-local' : 'other'
}

async function trace(url) {
  try {
    const r = await fetch(url, { signal: AbortSignal.timeout(5000) })
    return Object.fromEntries((await r.text()).trim().split('\n').map((l) => l.split('=')))
  } catch { return null }
}
async function ripestat(call, res) { // RIPEstat sometimes answers an HTML error page: retry
  for (let i = 0; i < 3; i++) {
    try {
      const r = await fetch(`https://stat.ripe.net/data/${call}/data.json?resource=${res}&sourceapp=cgnat-check`, { signal: AbortSignal.timeout(15000) })
      return (await r.json()).data
    } catch { await new Promise((ok) => setTimeout(ok, 2000)) }
  }
  return null
}
async function origin(ip) {
  const d = await ripestat('prefix-overview', ip)
  return d ? { prefix: d.resource, asn: d.asns?.[0]?.asn, holder: d.asns?.[0]?.holder } : {}
}

const local = Object.entries(os.networkInterfaces()).flatMap(([dev, list]) =>
  list.filter((a) => !a.internal).map((a) => ({ dev, ip: a.address, fam: a.family === 'IPv4' || a.family === 4 ? 4 : 6 })))
console.log('Local addresses:')
for (const a of local) console.log(`  ${a.dev.padEnd(8)} ${a.ip.padEnd(40)} ${a.fam === 4 ? kindV4(a.ip) : kindV6(a.ip)}`)

const t4 = await trace('https://1.1.1.1/cdn-cgi/trace')
const t6 = await trace('https://[2606:4700:4700::1111]/cdn-cgi/trace')
const pub4 = t4?.ip, pub6 = t6?.ip
console.log(`\nSeen from outside: IPv4 ${pub4 ?? 'none'}, IPv6 ${pub6 ?? 'none'}`)
if (pub4) {
  const o = await origin(pub4)
  console.log(`  ${pub4} in ${o.prefix ?? '?'} originated by AS${o.asn ?? '?'} ${o.holder ?? ''}`)
  if (o.asn === 14593) console.log('  AS14593 = SpaceX Starlink')
}

const v4local = local.filter((a) => a.fam === 4)
let verdict
if (pub4 && v4local.some((a) => a.ip === pub4)) verdict = 'IPv4: public address on this host, inbound possible (firewall permitting)'
else if (v4local.some((a) => kindV4(a.ip).startsWith('CGNAT'))) verdict = 'IPv4: CGNAT on this host, no inbound IPv4'
else if (pub4) verdict = 'IPv4: behind NAT (home router and maybe CGNAT upstream): inbound only via port forward on every NAT layer'
else verdict = 'IPv4: no IPv4 path out'
console.log(`\n${verdict}`)
if (pub6 && local.some((a) => a.ip === pub6)) console.log('IPv6: global address on this host, inbound depends on the router firewall')
else console.log('IPv6: no global IPv6 path seen')

// Optional: bun cgnat_check.js AS14593 - how much address space does a network announce?
const asArg = process.argv[2]
if (asArg) {
  const ps = (await ripestat('announced-prefixes', asArg)).prefixes.map((p) => p.prefix)
  const v4 = ps.filter((p) => !p.includes(':')).map((p) => { const [n, b] = p.split('/'); return { s: v4n(n), e: v4n(n) + 2 ** (32 - b) } })
    .sort((a, b) => a.s - b.s || b.e - a.e)
  let total = 0, end = -1
  for (const p of v4) { if (p.e > end) { total += p.e - Math.max(p.s, end); end = p.e } }
  const v6 = ps.filter((p) => p.includes(':'))
  const by = (l) => Object.entries(l.reduce((m, p) => ((m[p.split('/')[1]] = (m[p.split('/')[1]] || 0) + 1), m), {})).sort((a, b) => a[0] - b[0]).map(([k, n]) => `/${k} x${n}`).join(', ')
  console.log(`\n${asArg}: ${v4.length} IPv4 prefixes, ${total.toLocaleString('en')} unique addresses; ${v6.length} IPv6 prefixes`)
  console.log(`  IPv4 sizes: ${by(ps.filter((p) => !p.includes(':')))}`)
  console.log(`  IPv6 sizes: ${by(v6)}`)
}
bun cgnat_check.js            # this host only
bun cgnat_check.js AS14593    # plus the space a network announces

Salida, ejecutado en un servidor DCXV Cloud con Bun 1.4.4 el 2026-11-11 (octetos del host ocultos):

Local addresses:
  eth0     178.23.189.x                             public
  eth0     fe80::x                                  link-local
  int0     10.10.x.x                                private (RFC 1918)
  int0     fe80::x                                  link-local

Seen from outside: IPv4 178.23.189.x, IPv6 none
  178.23.189.x in 178.23.189.0/24 originated by AS204057 DCXV-AS DCXV INTERNATIONAL LTD

IPv4: public address on this host, inbound possible (firewall permitting)
IPv6: no global IPv6 path seen

AS14593: 2274 IPv4 prefixes, 990,208 unique addresses; 1130 IPv6 prefixes
  IPv4 sizes: /20 x2, /21 x28, /22 x58, /23 x1434, /24 x752
  IPv6 sizes: /36 x1, /38 x67, /40 x668, /41 x173, /42 x11, /44 x4, /45 x4, /46 x11, /47 x20, /48 x171

Nuestro servidor tiene su dirección pública en eth0, así que las conexiones entrantes le llegan en cuanto el firewall las permite; el IPv6 simplemente no está configurado en esta máquina. No tenemos línea Starlink, así que no mostramos salida de Starlink ni afirmamos nada sobre ella. Lo que hace el script allí: en un portátil tras el router de Starlink la dirección local es privada y la pública está en un prefijo de AS14593, así que informa de NAT e imprime la línea de Starlink; en el puerto WAN de un router de terceros ve directamente la dirección de 100.64.0.0/10 e informa de CGNAT.

Cuatro formas de llegar a un sitio tras Starlink

Forma ¿Entrada desde cualquiera? Qué necesita Qué la rompe
IPv6 directo al sitio Solo visitantes IPv6 Router propio (el de Starlink no tiene reglas de firewall) El /56 puede cambiar tras un traslado; muchas redes siguen siendo solo IPv4
Plan Priority, IPv4 pública Sí Un plan Priority La dirección puede cambiar tras un traslado o una actualización
VPN en malla (Tailscale, ZeroTier) Solo sus dispositivos Un agente en cada dispositivo No sirve para visitantes públicos ni equipos sin agente
Relé WireGuard en un servidor cloud Sí, IPv4 fija Un servidor pequeño con IPv4 estática Un salto más y una máquina más que mantener

El relé funciona porque CGNAT permite aquello para lo que se creó: las conexiones salientes. El sitio llama al relé, y PersistentKeepalive = 25 envía un paquete cada 25 segundos para que la entrada NAT siga abierta (la guía rápida de WireGuard da 25 s como intervalo que funciona con una gran variedad de firewalls). Los visitantes se conectan a la dirección del relé, que vive en un centro de datos y no se mueve con la antena.

El relé: dos archivos de configuración

No ejecutado aquí: requiere root en dos máquinas y un sitio remoto. Son las formas estándar de wg-quick; 192.0.2.10 es una dirección de documentación, ponga en su lugar la de su relé.

# /etc/wireguard/wg0.conf on the cloud server (public IPv4 192.0.2.10)
[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
PrivateKey = <relay-private-key>
# forward TCP 443 that arrives on the public address to the site
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.99.0.2:443
PostUp = iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
PostUp = iptables -A FORWARD -i eth0 -o wg0 -p tcp --dport 443 -j ACCEPT
PostUp = iptables -A FORWARD -i wg0 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 10.99.0.2:443
PostDown = iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE
PostDown = iptables -D FORWARD -i eth0 -o wg0 -p tcp --dport 443 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

[Peer]
# the site behind Starlink
PublicKey = <site-public-key>
AllowedIPs = 10.99.0.2/32
# /etc/wireguard/wg0.conf at the site (behind CGNAT)
[Interface]
Address = 10.99.0.2/24
PrivateKey = <site-private-key>

[Peer]
PublicKey = <relay-public-key>
Endpoint = 192.0.2.10:51820
AllowedIPs = 10.99.0.1/32
PersistentKeepalive = 25
wg genkey | tee private.key | wg pubkey > public.key
wg-quick up wg0

Tres notas. MASQUERADE hace que cada conexión llegue al sitio desde 10.99.0.1, así que sus registros pierden la dirección del visitante; si la necesita, quite esa regla y enrute las respuestas del sitio de vuelta por wg0. Cada paquete cruza ahora el enlace satelital y el relé, así que mida la latencia antes de poner voz o un lazo de control encima. Y prevea la caída del propio enlace: el corte global de Starlink de julio de 2025 duró más de dos horas.

Tamaño y precio del relé

Un relé reenvía paquetes y hace poco más. Nuestro plan cloud Small (2 vCPU, 4 GB de RAM, 40 GB, EUR 15,00 al mes, una IPv4 estática incluida) figura en nuestra propia lista de tareas para WireGuard con hasta 25 túneles; la página servidor VPN cubre el mismo montaje para equipos. Para flotas de dispositivos que necesitan una dirección cada uno, vea IoT y la escasez de IPv4.

Monte un relé con nosotros

Nuestros centros de datos Tier III en Praga y Covilha ofrecen servidores cloud, cada uno con una IPv4 estática. Inicie uno, ejecute cgnat_check.js en el sitio remoto y envíe la salida a sales@dcxv.com: le ayudaremos a montar el relé.

Starlink Mini 2 tras CGNAT: cómo llegar a un sitio que se mueve
networkingcloudinfrastructure

Starlink Mini 2 tras CGNAT: cómo llegar a un sitio que se mueve

Llega la Starlink Mini 2, pero los planes estándar van tras CGNAT. Contamos 990.208 IPv4 para más de 12 M de clientes y comparamos cuatro formas de acceso.

Transferencias IPv4 en LACNIC 2026: reglas, tarifas, un año
ipv4transfernetworking

Transferencias IPv4 en LACNIC 2026: reglas, tarifas, un año

Transferencias IPv4 en LACNIC en 2026: reglas de la sección 2.3.2.18, tarifa de US$ 1,037, NIC.br y NIC México, y un script con 1,929 transferencias 2016-2026.

Protestas contra centros de datos en Europa: qué hacer
clouddedicatedinfrastructure

Protestas contra centros de datos en Europa: qué hacer

El 6 de noviembre cientos entraron en la obra de Microsoft en Ámsterdam. Las cifras eléctricas de Países Bajos, latencia medida a hubs de la UE y qué hacer.

EE. UU. vota 45 a 1 contra los centros de datos: lecciones
clouddedicatedinfrastructure

EE. UU. vota 45 a 1 contra los centros de datos: lecciones

47 votaciones sobre centros de datos en EE. UU.: el lado restrictivo ganó 45. Recontamos los datos brutos y explicamos qué implica al alquilar servidores.

AWS BYOIP en 2026: $43.80 por IPv4 frente a tu propio /24
ipv4pricingcloud

AWS BYOIP en 2026: $43.80 por IPv4 frente a tu propio /24

AWS cobra $0.005 por hora por IPv4 pública, $43.80 al año. BYOIP no paga ese cargo: cuándo se amortiza un /24 comprado o alquilado, con un script de ROA.