Starlink Mini 2 за CGNAT: як дістатися до об'єкта, що переїжджає

Starlink Mini 2 за CGNAT: як дістатися до об'єкта, що переїжджає

Starlink Mini 2 за CGNAT: як дістатися до об'єкта, що переїжджає

Коротко (перевірено 2026-11-11): SpaceX відкрила передзамовлення Starlink Mini 2 за запрошеннями. Це портативна антена зі знімним акумулятором. Вона підключить до мережі більше камер, датчиків, польових ноутбуків і тимчасових офісів там, де немає оптики. Але стандартний тариф не дасть їм адреси, до якої можна підключитися з інтернету: IPv4 у Starlink за замовчуванням працює через операторський NAT (CGNAT), а вхідні з'єднання заблоковано. Ми порахували IPv4-простір, який анонсує мережа Starlink (990 208 адрес на понад 12 мільйонів клієнтів), написали скрипт, що показує, чи сидить хост за CGNAT, і порівняли чотири способи дістатися до об'єкта, який переїжджає.

Джерела. Mini 2 за даними Notebookcheck (Daniel Zlatev, 2026-11-10) і DishyTech (Colby Baber, 2026-11-09), обговорення на Hacker News: 369 балів і 380 коментарів на 2026-11-11. Політика адрес: статті Starlink Help Center "What IP address does Starlink provide?" і "IP Address", прочитано 2026-11-11. Дані маршрутизації: RIPEstat, запит нашим скриптом 2026-11-11.

Що нового в Mini 2

Параметр Mini 2, за повідомленнями від 2026-11-10
Акумулятор Знімний, 71 Вт-год, до 4,5 год (дані SpaceX), заряджання USB-C 60 Вт
Wi-Fi Wi-Fi 6 (у першої Mini був Wi-Fi 5)
Корпус IP67, 314 x 263 x 23,1 мм, 1,6 кг з акумулятором і підставкою
Доступність Передзамовлення за запрошенням для частини клієнтів у США, інші країни пізніше
Ціна Не визначена: комплект $249 і акумулятор $50 у коді сайту Starlink (DishyTech), в інших джерелах $999 без розбивки

Даних про швидкість не опубліковано. Для цієї статті головне саме акумулятор: антену, яка працює чотири години без мережі живлення, носять з об'єкта на об'єкт, і це змінює те, як ви дістаєтеся до всього, що стоїть за нею.

Проблема адрес: CGNAT за замовчуванням

Довідка Starlink описує дві політики IPv4. Стандартна - CGNAT: роутер отримує адресу з 100.64.0.0/10, спільного діапазону з RFC 6598, багато клієнтів ділять одну публічну адресу, а вхідний IPv4-трафік заборонено. Публічна IPv4-адреса доступна лише як опція на тарифах Priority (дві сторінки довідки називають їх по-різному: Local і Global Priority в одній, Priority, Mobile Priority і Maritime в іншій). Кожна лінія також отримує IPv6-префікс /56 через DHCPv6-PD, і Starlink пише, що нативний IPv6 працює на всіх її роутерах, комплектах і тарифах.

Для портативної антени важливі ще два рядки. Власні Wi-Fi-роутери Starlink не підтримують ні проброс портів, ні правила файрвола, ні для IPv4, ні для IPv6. А зарезервовані IPv4-адреса й IPv6-префікс переживають перезавантаження, але можуть змінитися, коли антену переносять або оновлюється її програмне забезпечення. Антена, що переїжджає, - це адреса, що переїжджає.

Чому адрес не вистачає на всіх: наш підрахунок

Наш скрипт запитав у RIPEstat, що анонсує AS14593 (SPACEX-STARLINK): 2 274 IPv4-префікси, які покривають 990 208 унікальних адрес, переважно /23 і /24. У червні 2026 року Starlink повідомила про понад 12 мільйонів активних клієнтів (Mezha). Це приблизно одна публічна IPv4-адреса на 12 клієнтів. За 90-денною медіаною ip4.market для /24, $25,25 за адресу (індекс оновлено 2026-11-02, див. ціни на IPv4), одна адреса на клієнта коштувала б близько $303 мільйонів; простір, який анонсує AS14593, за цією ціною коштує близько $25 мільйонів. З IPv6 картина протилежна: 1 130 префіксів, з них 668 - /40, а один /40 вміщує 65 536 клієнтських /56, тож лише ці /40 вміщують близько 43,8 мільйона.

Межі підрахунку: RIPEstat не враховує маршрути, які бачать менше ніж 10 пірів RIS, Starlink може пропускати частину трафіку через інші AS, а анонсований простір - не те саме, що використаний. Але саме це співвідношення пояснює, чому CGNAT стоїть за замовчуванням.

Скрипт: чи цей хост за CGNAT?

cgnat_check.js показує адреси хоста та їхній тип (публічна, RFC 1918, 100.64.0.0/10), питає в публічної точки, яку адресу вона бачить, визначає мережу, що її анонсує, і каже, чи може працювати вхідний IPv4. З номером AS як аргументом він ще й підсумовує простір, який анонсує ця мережа, об'єднуючи префікси, що перекриваються.

// 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

Результат, запущено на хмарному сервері DCXV з Bun 1.4.4 2026-11-11 (октети хоста приховано):

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

Наш сервер тримає публічну адресу на eth0, тож вхідні з'єднання доходять до нього, щойно їх дозволить файрвол; IPv6 на цій машині просто не налаштовано. Лінії Starlink у нас немає, тому виводу зі Starlink ми не показуємо і нічого про нього не стверджуємо. Що робить скрипт там: на ноутбуці за роутером Starlink локальна адреса приватна, а публічна лежить у префіксі AS14593, тож він повідомляє про NAT і друкує рядок про Starlink; на WAN-порту стороннього роутера він бачить адресу з 100.64.0.0/10 напряму й повідомляє про CGNAT.

Чотири способи дістатися до об'єкта за Starlink

Спосіб Вхідні від будь-кого? Що потрібно Що ламає
IPv6 напряму до об'єкта Лише IPv6-відвідувачі Власний роутер (у Starlink немає правил файрвола) /56 може змінитися після переїзду; багато мереж досі лише IPv4
Тариф Priority, публічна IPv4 Так Тариф Priority Адреса може змінитися після переїзду чи оновлення ПЗ
Mesh VPN (Tailscale, ZeroTier) Лише ваші пристрої Агент на кожному пристрої Не для публічних відвідувачів і пристроїв без агента
Ретранслятор WireGuard на хмарному сервері Так, фіксована IPv4 Невеликий сервер зі статичною IPv4 Ще один хоп і ще одна машина, яку треба підтримувати

Ретранслятор працює, бо CGNAT дозволяє те, для чого його створено: вихідні з'єднання. Об'єкт сам підключається до ретранслятора, а PersistentKeepalive = 25 надсилає пакет кожні 25 секунд, щоб запис NAT лишався відкритим (у quick start WireGuard 25 с названо інтервалом, що працює з широким колом файрволів). Відвідувачі підключаються до адреси ретранслятора, яка живе в дата-центрі й не переїжджає разом з антеною.

Ретранслятор: два файли конфігурації

Тут не запускалося: потрібен root на двох машинах і віддалений об'єкт. Це стандартні форми wg-quick; 192.0.2.10 - документаційна адреса, підставте адресу свого ретранслятора.

# /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

Три примітки. MASQUERADE робить так, що кожне з'єднання приходить на об'єкт з 10.99.0.1, і журнали об'єкта втрачають адресу відвідувача; якщо вона потрібна, приберіть це правило й маршрутизуйте відповіді об'єкта назад через wg0. Кожен пакет тепер проходить і супутниковий канал, і ретранслятор, тож виміряйте затримку, перш ніж пускати туди голос чи контур керування. І плануйте на випадок падіння самого каналу: глобальний збій Starlink у липні 2025 року тривав понад дві години.

Розмір і ціна ретранслятора

Ретранслятор пересилає пакети й майже нічого більше не робить. Наш хмарний тариф Small (2 vCPU, 4 ГБ RAM, 40 ГБ, EUR 15,00 на місяць, одна статична IPv4 у ціні) у нашому ж переліку задач розрахований на WireGuard до 25 тунелів; сторінка VPN-сервер описує таке саме налаштування для команд. Для парку пристроїв, кожному з яких потрібна адреса, див. IoT і дефіцит IPv4.

Запустіть ретранслятор у нас

Наші дата-центри Tier III у Празі та Ковільяні мають хмарні сервери, кожен зі статичною IPv4. Запустіть сервер, виконайте cgnat_check.js на віддаленому об'єкті й надішліть результат на sales@dcxv.com: ми допоможемо налаштувати ретранслятор.

Starlink Mini 2 за CGNAT: як дістатися до об'єкта, що переїжджає
networkingcloudinfrastructure

Starlink Mini 2 за CGNAT: як дістатися до об'єкта, що переїжджає

Starlink Mini 2 вийшла, але стандартні тарифи сидять за CGNAT. Ми порахували 990 208 IPv4 на 12+ млн клієнтів і порівняли чотири способи дістатися до об'єкта.

Трансфер IPv4 у LACNIC у 2026: правила, збори, рік утримання
ipv4transfernetworking

Трансфер IPv4 у LACNIC у 2026: правила, збори, рік утримання

Трансфери IPv4 у LACNIC у 2026: правила розділу 2.3.2.18, збір US$ 1,037, NIC.br і NIC México та скрипт за 1,929 трансферами з журналу LACNIC 2016-2026 років.

Протести проти дата-центрів у Європі: що робити клієнтам
clouddedicatedinfrastructure

Протести проти дата-центрів у Європі: що робити клієнтам

6 листопада сотні людей увійшли на будмайданчик дата-центру Microsoft в Амстердамі. Цифри мережі Нідерландів, виміряна затримка до хабів ЄС і поради клієнтам.

Виборці США відхилили дата-центри 45 до 1: уроки покупцям
clouddedicatedinfrastructure

Виборці США відхилили дата-центри 45 до 1: уроки покупцям

47 голосувань у США щодо дата-центрів: обмежувальна сторона виграла 45. Ми перерахували сирі дані й пояснюємо, що це означає для тих, хто орендує сервери.

AWS BYOIP у 2026: $43.80 за IPv4 чи власний блок /24
ipv4pricingcloud

AWS BYOIP у 2026: $43.80 за IPv4 чи власний блок /24

AWS бере $0.005 на годину за публічну IPv4, $43.80 на рік. BYOIP без цієї плати: коли куплений чи орендований /24 окупається, і скрипт перевірки ROA.