Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi

Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi

Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi

W skrócie (sprawdzone 2026-11-11): SpaceX otworzył przedsprzedaż Starlink Mini 2 na zaproszenia. To przenośna antena z wyjmowanym akumulatorem. Podłączy do sieci więcej kamer, czujników, laptopów terenowych i tymczasowych biur tam, gdzie nie ma światłowodu. Czego standardowy plan im nie da, to adresu osiągalnego z internetu: IPv4 w Starlink działa domyślnie przez NAT operatorski (CGNAT), a połączenia przychodzące są zablokowane. Policzyliśmy przestrzeń IPv4 ogłaszaną przez sieć Starlink (990 208 adresów na ponad 12 milionów klientów), napisaliśmy skrypt, który mówi, czy host jest za CGNAT, i porównaliśmy cztery sposoby dotarcia do miejsca, które się przemieszcza.

Źródła. Mini 2 według Notebookcheck (Daniel Zlatev, 2026-11-10) i DishyTech (Colby Baber, 2026-11-09), dyskusja na Hacker News: 369 punktów i 380 komentarzy na 2026-11-11. Polityka adresów: artykuły Starlink Help Center "What IP address does Starlink provide?" i "IP Address", przeczytane 2026-11-11. Dane routingu: RIPEstat, odpytany przez nasz skrypt 2026-11-11.

Co nowego w Mini 2

Element Mini 2, według doniesień z 2026-11-10
Akumulator Wyjmowany, 71 Wh, do 4,5 h (dane SpaceX), ładowanie USB-C 60 W
Wi-Fi Wi-Fi 6 (pierwsza Mini miała Wi-Fi 5)
Obudowa IP67, 314 x 263 x 23,1 mm, 1,6 kg z akumulatorem i podpórką
Dostępność Przedsprzedaż na zaproszenie dla wybranych klientów w USA, kolejne kraje później
Cena Nieustalona: zestaw za $249 i akumulator za $50 w kodzie strony Starlink (DishyTech), gdzie indziej $999 bez rozbicia

Nie opublikowano danych o przepustowości. Dla tego artykułu liczy się akumulator: antena działająca cztery godziny bez sieci elektrycznej to antena noszona z miejsca na miejsce, a to zmienia sposób, w jaki dociera się do tego, co stoi za nią.

Problem adresów: domyślnie CGNAT

Strony pomocy Starlink opisują dwie polityki IPv4. Domyślna to CGNAT: router dostaje adres z 100.64.0.0/10, współdzielonego zakresu z RFC 6598, wielu klientów dzieli każdy adres publiczny, a przychodzący ruch IPv4 jest niedozwolony. Publiczny adres IPv4 jest opcją tylko w planach Priority (dwie strony pomocy nazywają je różnie: Local i Global Priority w jednej, Priority, Mobile Priority i Maritime w drugiej). Każde łącze dostaje też prefiks IPv6 /56 przez DHCPv6-PD, a Starlink pisze, że natywne IPv6 działa na wszystkich jej routerach, zestawach i planach.

Dla przenośnej anteny ważne są jeszcze dwa zdania. Własne routery Wi-Fi Starlink nie obsługują przekierowania portów ani reguł zapory, ani dla IPv4, ani dla IPv6. A zarezerwowany adres IPv4 i prefiks IPv6 przetrwają restart, ale mogą się zmienić, gdy antena zostanie przeniesiona albo zaktualizuje się jej oprogramowanie. Antena, która się przenosi, to adres, który się przenosi.

Dlaczego adresów nie starcza dla wszystkich: nasze liczenie

Nasz skrypt zapytał RIPEstat, co ogłasza AS14593 (SPACEX-STARLINK): 2 274 prefiksy IPv4 obejmujące 990 208 unikalnych adresów, głównie /23 i /24. W czerwcu 2026 roku Starlink podał, że ma ponad 12 milionów aktywnych klientów (Mezha). To mniej więcej jeden publiczny adres IPv4 na 12 klientów. Przy 90-dniowej medianie ip4.market dla /24, $25,25 za adres (indeks zaktualizowany 2026-11-02, zob. ceny IPv4), jeden adres na klienta kosztowałby około $303 milionów; przestrzeń ogłaszana przez AS14593 jest przy tej cenie warta około $25 milionów. W IPv6 jest odwrotnie: 1 130 prefiksów, w tym 668 /40, a jeden /40 mieści 65 536 klienckich /56, więc same te /40 mieszczą około 43,8 miliona.

Granice liczenia: RIPEstat pomija trasy widziane przez mniej niż 10 peerów RIS, Starlink może prowadzić część ruchu przez inne numery AS, a przestrzeń ogłoszona to nie to samo co używana. Mimo to ta proporcja tłumaczy, czemu CGNAT jest domyślny.

Skrypt: czy ten host jest za CGNAT?

cgnat_check.js wypisuje adresy hosta i ich rodzaj (publiczny, RFC 1918, 100.64.0.0/10), pyta publiczny punkt, jaki adres widzi, ustala sieć, która go ogłasza, i mówi, czy przychodzące IPv4 może działać. Z numerem AS jako argumentem dodatkowo sumuje przestrzeń ogłaszaną przez tę sieć, scalając nakładające się prefiksy.

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

Wynik, uruchomione na serwerze DCXV Cloud z Bun 1.4.4 dnia 2026-11-11 (oktety hosta zamaskowane):

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

Nasz serwer ma publiczny adres na eth0, więc połączenia przychodzące docierają do niego, gdy tylko zapora je dopuści; IPv6 po prostu nie jest na tej maszynie skonfigurowane. Nie mamy łącza Starlink, więc nie pokazujemy wyniku ze Starlink i nic o nim nie twierdzimy. Co skrypt robi tam: na laptopie za routerem Starlink adres lokalny jest prywatny, a publiczny leży w prefiksie AS14593, więc zgłasza NAT i drukuje linię o Starlink; na porcie WAN routera innej firmy widzi adres z 100.64.0.0/10 bezpośrednio i zgłasza CGNAT.

Cztery sposoby dotarcia do miejsca za Starlink

Sposób Przychodzące od każdego? Czego wymaga Co go psuje
IPv6 prosto do miejsca Tylko goście z IPv6 Własny router (router Starlink nie ma reguł zapory) /56 może się zmienić po przeniesieniu; wiele sieci to nadal tylko IPv4
Plan Priority, publiczny IPv4 Tak Plan Priority Adres może się zmienić po przeniesieniu lub aktualizacji
Mesh VPN (Tailscale, ZeroTier) Tylko Twoje urządzenia Agent na każdym urządzeniu Nie dla publicznych gości ani urządzeń bez agenta
Przekaźnik WireGuard na serwerze cloud Tak, stały IPv4 Mały serwer ze statycznym IPv4 Jeden skok więcej i jedna maszyna więcej do utrzymania

Przekaźnik działa, bo CGNAT pozwala na to, do czego go zbudowano: połączenia wychodzące. Miejsce samo łączy się z przekaźnikiem, a PersistentKeepalive = 25 wysyła pakiet co 25 sekund, żeby wpis NAT pozostał otwarty (quick start WireGuard podaje 25 s jako interwał działający z szerokim wachlarzem zapór). Goście łączą się z adresem przekaźnika, który mieszka w centrum danych i nie przenosi się razem z anteną.

Przekaźnik: dwa pliki konfiguracyjne

Tu nieuruchamiane: wymaga roota na dwóch maszynach i zdalnego miejsca. To standardowe formy wg-quick; 192.0.2.10 to adres dokumentacyjny, wstaw adres własnego przekaźnika.

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

Trzy uwagi. MASQUERADE sprawia, że każde połączenie dociera do miejsca z 10.99.0.1, więc jego logi tracą adres gościa; jeśli jest potrzebny, usuń tę regułę i kieruj odpowiedzi miejsca z powrotem przez wg0. Każdy pakiet przechodzi teraz przez łącze satelitarne i przekaźnik, więc zmierz opóźnienie, zanim puścisz tamtędy głos lub pętlę sterowania. I zaplanuj awarię samego łącza: globalna awaria Starlink w lipcu 2025 roku trwała ponad dwie godziny.

Rozmiar i cena przekaźnika

Przekaźnik przekazuje pakiety i niewiele więcej. Nasz plan cloud Small (2 vCPU, 4 GB RAM, 40 GB, EUR 15,00 miesięcznie, jeden statyczny IPv4 w cenie) na naszej własnej liście zadań jest przewidziany dla WireGuard z maksymalnie 25 tunelami; strona serwer VPN opisuje tę samą konfigurację dla zespołów. Dla flot urządzeń, z których każde potrzebuje adresu, zob. IoT i niedobór IPv4.

Uruchom przekaźnik u nas

Nasze centra danych Tier III w Pradze i Covilhã utrzymują serwery cloud, każdy ze statycznym IPv4. Uruchom serwer, wykonaj cgnat_check.js w zdalnym miejscu i wyślij wynik na sales@dcxv.com: pomożemy skonfigurować przekaźnik.

Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi
networkingcloudinfrastructure

Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi

Starlink Mini 2 już jest, ale standardowe plany stoją za CGNAT. Policzyliśmy 990 208 IPv4 na 12+ mln klientów i porównaliśmy cztery drogi do zdalnego miejsca.

Transfery IPv4 w LACNIC w 2026: zasady, opłaty, rok blokady
ipv4transfernetworking

Transfery IPv4 w LACNIC w 2026: zasady, opłaty, rok blokady

Transfery IPv4 w LACNIC w 2026: zasady sekcji 2.3.2.18, opłata US$ 1,037, NIC.br i NIC México oraz skrypt analizujący 1,929 transferów z lat 2016-2026.

Protesty przeciw centrom danych w Europie: co robić
clouddedicatedinfrastructure

Protesty przeciw centrom danych w Europie: co robić

6 listopada setki osób weszły na budowę centrum danych Microsoft w Amsterdamie. Liczby holenderskiej sieci, zmierzone opóźnienia do hubów UE i co robić.

Centra danych przegrały 45 do 1 w USA: wnioski dla klientów
clouddedicatedinfrastructure

Centra danych przegrały 45 do 1 w USA: wnioski dla klientów

47 lokalnych głosowań w USA o centrach danych, strona restrykcyjna wygrała w 45. Przeliczamy surowe dane i wyjaśniamy, co to oznacza dla najemców serwerów.

AWS BYOIP w 2026: $43.80 za IPv4 czy własna pula /24
ipv4pricingcloud

AWS BYOIP w 2026: $43.80 za IPv4 czy własna pula /24

AWS pobiera $0.005 za godzinę za publiczny IPv4, $43.80 rocznie. BYOIP jest wolny od tej opłaty: kiedy kupiony lub dzierżawiony /24 się zwraca, ze skryptem.