Packet Path/

CGNAT Explained: Why Thousands of Customers Share One Public IP Address

Your home router already shares one public address among every device in your house. A provider doing CGNAT does the same trick one level up: hundreds or thousands of households share one public IPv4 address, and the provider tells them apart by port number. That one change explains why port forwarding mysteriously stops working, why an IP address alone no longer identifies a subscriber, and why sizing a CGNAT pool is really an exercise in dividing 64,512 usable ports into blocks nobody wastes.

Routing · Intermediate · 11 min · October 29, 2026

Editorial illustration on a dark teal background: seven small coloured houses on the left connected by glowing lines into a large grey apartment-style building in the centre, which connects to a single glowing doorway on the right opening onto a network mesh; above the doorway sit seven coloured envelopes matching the houses.

The short answer

CGNAT is address sharing, run by the provider. NAT (Network Address Translation) is the trick your home router uses: every device in the house has a private address, but the outside world sees one public address, and the router remembers which device each conversation belongs to. CGNAT — carrier-grade NAT, also called large-scale NAT — moves that same trick up one level. The provider gives your router an address that is not public either, then runs its own, much bigger NAT in front of hundreds or thousands of customers at once. From the internet's point of view, all of those households are the same public address. What tells them apart is the port: the number attached to each connection alongside the address. One public address plus one port points at exactly one subscriber's connection — and that pair, address and port together, is the whole game this article is about.

Think of a large apartment building with one street address. The street address alone does not identify anyone; the flat number does. CGNAT is the same building at internet scale: the public IP address is the street address, and the port is the flat number. Everything that follows — the sizing maths, the logging problem, the reason your port-forwarding rule stopped working — comes from the fact that there is only one street address for the whole building, and a fixed number of flats inside it.

In plain EnglishCGNAT: carrier-grade NAT — a provider sharing its public IPv4 addresses among many subscribers at once. Port: a number from 0 to 65,535 attached to a connection; together, address + port identify one conversation. Shared address space: 100.64.0.0/10, the range set aside by RFC 6598 for the link between a subscriber and the provider's CGNAT, so it cannot clash with home-network ranges. Port block allocation (PBA): handing a subscriber a whole block of ports at a time instead of one port per connection. Deterministic NAT: a fixed, calculable assignment of blocks to subscribers, so a public address + port can be worked back to a subscriber by arithmetic rather than by searching logs.

Why providers ran out of addresses

IPv4 addresses are 32-bit numbers, so there are about 4.3 billion of them in total, and the free pool is gone: IANA handed out its last free blocks to the regional registries in February 2011. A growing provider — above all a mobile provider, where every phone is a subscriber — simply cannot give every customer a public IPv4 address of their own. IPv6 is the long-term answer, and providers run it alongside, but the IPv4 internet still has to work today. So the provider does what your router does: it puts subscribers behind a translator and shares.

One detail had to be fixed first. The address the provider gives your router cannot be one of the usual private ranges (192.168.x.x, 10.x.x.x), because your own home network may already use exactly that range on the inside, and the same numbers on both sides of a router cause collisions. RFC 6598 solved it by reserving a dedicated range, 100.64.0.0/10 — addresses from 100.64.0.0 to 100.127.255.255 — as shared address space for exactly this provider-to-subscriber link. That gives you a practical test, too: if your router's WAN address sits in 100.64.0.0/10 and the public address a website sees is different, you are behind CGNAT right now.

HOUSEHOLD A HOUSEHOLD B HOUSEHOLD C CARRIER-GRADE NATone translator, many customers ONE PUBLIC ADDRESSports tell subscribers apart

Ports are the real currency

A NAT sharing one address can only tell subscribers apart by giving each connection a different public port. Ports run from 0 to 65,535, and in the common CGNAT setup the usable range starts at 1024 — the well-known ports below that stay reserved — leaving 64,512 usable ports per public address. That number, not the number of addresses, is what a CGNAT design actually spends. Every open connection holds one port for as long as it lasts, so a subscriber's demand is measured in peak simultaneous ports: a quiet household might peak at a few dozen, a house full of devices, games and cloud backups at a few hundred.

Two consequences follow immediately. First, one heavy subscriber can hurt everyone sharing the address: if a single customer could grab unlimited ports, they could exhaust the pool and everyone else's new connections would fail. That is why the CGNAT requirements in RFC 6888 include a limit on how many ports each subscriber may use. Second, capacity is a division problem: subscribers per address equals usable ports divided by ports per subscriber — with all the rounding pain that dividing into fixed blocks brings, which is where the design gets interesting.

Port blocks: why the NAT deals in bundles

The naive design gives out one port at a time, per connection. It uses ports efficiently, but it creates a second problem that has nothing to do with capacity: logging. When a website or an authority asks "which subscriber used this public address at this time?", the provider can only answer if it recorded the mapping. Logging every single connection sounds thorough until you price it. Using the planner's own estimator inputs as an illustration — 10,000 subscribers, 500 sessions per subscriber per day, 200 bytes per log record — per-session logging produces 1 GB of logs a day, 30 GB a month, and real providers are far larger than 10,000 subscribers. One analysis cited in RFC 7768 put per-session logging for 20,000 subscribers at 42.5 TB over 60 days.

Port block allocation is the standard answer. Instead of a port at a time, the NAT hands a subscriber a whole block — say 512 consecutive ports on one public address — and writes one log record for the block. Every connection inside that block is covered by that one record, so the log volume falls by roughly the block size: the same RFC 7768 analysis put block-based logging at 40.6 GB for the case above, against 42.5 TB per session. When the subscriber outgrows the block, they get another one, and one more record. RFC 6888 states the trade-off plainly as three competing requirements for any port-allocation scheme: maximise port utilisation, minimise log volume, and make port numbers hard for attackers to guess. You cannot have all three at once; block size is where you choose your balance.

Deterministic NAT pushes the logging idea to its limit. Blocks are assigned to subscribers by a fixed rule instead of on demand, so given a public address, a port and the configuration, the operator calculates which subscriber it was — the mapping needs no per-session or per-block log at all (the configuration itself is what is recorded). The price is flexibility: the assignment is fixed whether a subscriber uses their ports or not, so deterministic designs waste more ports to buy near-zero mapping logs.

One public address: 64,512 usable ports (1024-65535) BLOCK 1 · 512 BLOCK 2 · 512 BLOCK 3 · 512 … 126 blocks in total Same address, 2,048-port blocks: 31 blocks fit — and 1,024 ports are left over 31 blocks × 2,048 = 63,488 ports assigned 1,024 STRANDED PORTS

A worked sizing example, with the planner's numbers

Here is the arithmetic a provider actually runs, using exactly the worked example built into the free planner linked below. Start with a small pool: 16 public addresses, the usual usable range 1024-65535 (64,512 ports per address), and 512-port blocks.

Step one: blocks per address. A block must be consecutive ports on one address — it cannot straddle two — so the division is done per address and rounded down: 64,512 ÷ 512 = 126 blocks per address, exactly, with nothing left over in this case. Step two: total blocks. 126 × 16 addresses = 2,016 blocks. Step three: blocks per subscriber. If a subscriber peaks at 300 ports, one 512-port block covers the peak, so each subscriber needs one block. Guaranteed capacity is therefore 2,016 subscribers — 126 per public address — where "guaranteed" has a precise meaning: every one of them can hit their 300-port peak at the same moment and nobody runs out.

Step four: the waste, stated honestly. Each subscriber is allocated 512 ports and peaks at 300, so 212 ports per subscriber sit unused — a utilisation of 300 ÷ 512 = 58.6%. That waste is the price of the logging win: bigger blocks mean fewer log records and more waste. Watch the trade move: give the same pool a subscriber who peaks at 1,000 ports with 256-port blocks and the maths tightens dramatically — 252 blocks per address, 25,200 blocks across 100 addresses in the planner's second vector, four blocks per subscriber (4 × 256 = 1,024 allocated against a 1,000 peak), 6,300 guaranteed subscribers, only 24 wasted ports each, 97.7% utilisation — but now a busy subscriber triggers four block allocations, and four times the block-log records of a one-block design. Neither answer is "efficient" in the abstract; each is a chosen point on the RFC 6888 trade-off.

Step five: stranded ports, the waste people forget. Rounding down per address leaves a remainder whenever the block size does not divide the range exactly. With 2,048-port blocks, 64,512 ÷ 2,048 = 31.5, so 31 blocks fit and 1,024 ports per address can never be assigned to anyone — stranded. This is also why pooling the arithmetic is a trap: multiply all the ports in a big pool together and divide once, and you get a larger, wrong answer, because real blocks cannot span addresses. Any sizing that does not show its stranded ports is hiding part of its cost.

And a warning about the other capacity number tools show: a statistical capacity, based on the average blocks subscribers actually hold rather than their peak. It is a legitimate planning figure — peaks do not all land at once — but it is oversubscription by definition. If everyone peaks together, the statistical pool runs out first. A design is only honest when both numbers are printed side by side and the guaranteed one is the one the promise is made on.

What CGNAT breaks, felt from the subscriber side

Most subscribers never notice CGNAT — ordinary browsing, streaming and messaging are all outbound connections, which NAT handles fine. The breakage is specific. Inbound connections are the big one: a port-forwarding rule on your home router cannot work on its own, because the provider's NAT in front of it has no mapping for your traffic and does not take instructions from your router — hosting a game server, a camera or a home VPN server over IPv4 stops working unless the provider offers a public address, an opt-out, or a protocol such as PCP to request a mapping. Shared reputation is the quieter one: if one subscriber behind an address abuses a website, the block or rate-limit lands on the address — and therefore on everyone sharing it. And identification changes meaning: an IP address in a log no longer names a subscriber, or even a household. Address, port and an accurate timestamp are the smallest unit that does — which is the whole reason the logging design above matters.

Four CGNAT mistakes that actually happen

1. Sizing on averages and calling it capacity. Average ports per subscriber is a fine input for a statistical estimate, but the number subscribers experience on the worst evening is the peak-based guaranteed figure. Publish both, promise on the guaranteed one.

2. Pooling the ports before dividing. Multiplying pool ports together and dividing by block size once ignores that blocks live on single addresses, and silently invents capacity out of the stranded remainders. Always floor per address first, then multiply.

3. Choosing huge blocks just to cut the logs. Log volume falls as blocks grow, but so does utilisation — a 512-port block for a 300-port peak wastes 212 ports before anyone has done anything wrong, and 4,096-port blocks strand 3,072 ports per address on the standard range. The block size is a three-way trade (utilisation, logs, guessability), not a logging-only dial.

4. Forgetting that identification needs the port. Keeping per-session logs "for compliance" while recording only addresses and timestamps misses the point of a shared address: without the source port, the record cannot identify anyone at all. The allocation scheme and the log format have to be designed together.

Fact-check sourcesShared address space 100.64.0.0/10 (100.64.0.0-100.127.255.255) and why RFC 1918 space could not be reused on the provider link: RFC 6598. Per-subscriber port limits and the three competing port-allocation requirements (maximise utilisation, minimise log volume, make ports hard to guess): RFC 6888, requirements REQ-13 to REQ-15. Log-volume comparison for 20,000 subscribers over 60 days (42.5 TB per-session vs 40.6 GB port-block): the NAT64 port-allocation analysis cited in RFC 7768. All sizing figures (64,512 usable ports; 126 blocks of 512; 2,016 guaranteed subscribers on 16 addresses at a 300-port peak; 212 wasted ports; 1,024 stranded ports at 2,048-port blocks; the 10,000-subscriber logging estimate at 500 sessions/day and 200 bytes/record) are the CGNAT planner's own verified vectors, reproduced step by step in the text.

Try it yourself

Run the numbers from this article in the free CGNAT Port-Block Planner — it starts on exactly the worked example above (16 public IPs, 512-port blocks, a 300-port peak: 2,016 subscribers guaranteed). Then drag the block size up to 2,048 and watch the stranded ports appear, switch the logging estimator between per-session, per-block and deterministic, and use its reverse lookup to work a public address and port back to the subscriber block it belongs to.

Key takeaways

Keep reading