back to deep dive

Deploying CGN at ISP scale: Huawei NE8000 vs FortiGate 1000F

Carrier-Grade NAT solves one problem — sharing scarce IPv4 across many subscribers — but how you deploy it decides whether it's a calm utility or a 3 a.m. pager. This is how we deploy CGN / NAT444 properly at ISP scale on two cost-effective platforms: a carrier edge router with a dedicated NAT service board (Huawei NetEngine 8000 M8 + VSUP-100), and a next-gen firewall doing CGN alongside security (FortiGate 1000F, NP7-based). Both are shown side by side, but the important part isn't the box — it's the redundancy model, so we lead with that.

Both configs are anonymised. Public pools use RFC 5737 documentation space (203.0.113.0/24, 198.51.100.0/24); subscriber space is the real RFC 6598 range (100.64.0.0/10); ASNs use documentation AS 64500; all secrets and identifiers are redacted. Private 10.x link/loopback addresses are illustrative.

The redundancy model: two boxes, source-hashed, pure L3

The instinct is to reach for stateful HA — VRRP, an inter-chassis session-sync, an active/standby pair. We deliberately don't, for CGN. Instead we run two independent CGN nodes, each owning a different public subnet, and load-share subscribers across them with source-IP hash ECMP from the core/BNG. No VRRP, no shared session state, no split-brain — just L3 routing.

PUBLIC · EDGE INSIDE · SUBS internet BGP default-route in pools out → edge cgn-01 pool A 203.0.113.0/24 cgn-02 pool B 198.51.100.0/24 bng-01 bng-02 source-IP hash on the BNG a subscriber → one CGN subscribers · 100.64.0.0/10
Subscriber traffic enters on the inside (subscribers → bng-01/02). The BNG applies source-IP-hash ECMP, pinning each subscriber to one of the two CGN nodes. Each CGN node owns a distinct public subnet advertised toward the edge via BGP + a blackhole route; OSPF stays on the p2p links and loopbacks. A node failure just withdraws its routes and rehashes those subscribers onto the survivor.

Why this beats VRRP/stateful HA for a CGN:

  • Source-IP hashing pins a subscriber to one box, so its NAT mappings are stable — no per-flow flapping between nodes, no need to sync state.
  • Failure is just routing. If cgn-01 dies, its pool and links withdraw from BGP/OSPF, the core reconverges, and those subscribers rehash onto cgn-02. They re-NAT behind a new public IP and reconnect in seconds — no split-brain, nothing to fail over by hand.
  • It scales by addition. Need more capacity? Add a third node and a third public subnet. The hash spreads onto it. No pair to rebuild.
  • It's the same on both platforms. With the FortiGate you deploy multiple units anyway — this model means they're plain independent routers, not a fragile cluster.
Stateful cross-box HA does exist (Huawei service-ha, FortiGate FGCP) and preserves sessions through a failover. We reach for it only when a few seconds of reconnect is genuinely unacceptable. For most ISPs it adds operational complexity that this pure-L3 model simply doesn't have.

Colour code for the columns below: orange = the Huawei carrier way, green = the FortiGate way. Commands are verbatim-accurate; the surrounding explanation is ours.

In-box redundancy — and why we still run two

The two-box model above covers box and site failure. Within a single chassis, the two platforms are worlds apart in what they survive on their own — and that changes how much the "second box" is really carrying.

// Huawei NE8000 M8 — redundant on its own

The M8 is a modular chassis that's already internally redundant: dual MPUs (active/standby control), redundant fabric / PSUs / fans, multiple line cards, and room for two VSUP service cards for 1+1 NAT-board backup. You enable it in-box:

! two VSUP cards — slot 7 active, slot 8 hot standby
service-location 7
 location slot 7 backup slot 8
!
! license the NAT feature on BOTH slots (or the standby fails over cold)
license
 active nat session-table size 3 slot 7
 active nat session-table size 3 slot 8
!
! mirror the live NAT session table to the standby board
service-ha hot-backup enable
service-ha preempt-time 360

So one M8 already rides through a VSUP card failure (1+1, sessions intact), an MPU failure, and line-card / PSU / fan failures — no second box needed for any of those.

// FortiGate 1000F — fixed appliance

The 1000F is a fixed appliance — no line cards, no service boards. You get redundant PSUs and the NP7s, but there's no in-chassis NAT failover. Its "1+1" lives at the box level: a second unit in FGCP, or — in our model — a second independent source-hashed node.

That's the crux: on the FortiGate the second box is your only redundancy; on the Huawei it's an extra layer on top of a chassis that's already hardened internally.

We still deploy two boxes either way. In-box redundancy — even the M8's — doesn't cover software crashes, a bad upgrade, a power or site event, or a fat-fingered change. The two-box, source-hashed L3 model covers exactly the failures a single chassis can't. On Huawei you simply get both: internal 1+1 and a second node.

1 — Where the NAT actually runs

The biggest architectural difference, and it colours everything else. Huawei offloads NAT to a purpose-built service card; FortiGate runs it in the NP7 network processors alongside firewalling, isolated in its own VDOM.

// Huawei NE8000 M8 · VSUP-100

A dedicated VSUP-100 service board does the NAT. Everything binds to its service-location.

vsm on-board-mode disable
service-location 7
 location slot 7 backup slot 8
service-instance-group CGN-SLOT-7
 service-location 7
// FortiGate 1000F · NP7

NAT runs in the NP7 ASICs; the CGN lives in its own VDOM. A caveat that lives in global config, not the CGN VDOM: NPU queue tuning so control/HA traffic is never starved by dataplane.

config system npu
    set dedicated-management-cpu enable
    config np-queues
        config ip-service
            edit "BGP"
                set queue 9
            next
            edit "BFD-single-hop"
                set queue 11
        end
    end
end
Router vs firewall — the real platform distinction. The Huawei is a router that happens to do NAT. The FortiGate is a firewall that happens to do NAT, so in the same box you also get stateful firewalling / IPS and much stronger DPI-based traffic shaping (application-aware, full deep packet inspection) — things a router simply doesn't offer. And on larger FortiGate models there's a dedicated hardware CGN mode (hyperscale) that pushes NAT scale well past what a 1000F handles.

2 — Address pools & port allocation

Each node's public subnet is shared across its subscribers by handing each subscriber a block of ports (not per-flow ports) — that's what keeps CGN logging sane. The knobs line up almost one-to-one.

// Huawei

Port budget is set on the nat instance: a base block plus extended blocks under load.

nat ip-pool CGN-GPON-FIBER-ACCESS-pool
 section 0 203.0.113.0 mask 24
#
nat instance CGN id 1
 port-range 2048 extended-port-range 8192 extended-times 3
 nat ip access-user limit 16
 nat filter mode full-cone
 port-reuse enable
// FortiGate

An ippool of type port-block-allocation: 1888 ports per block, up to 4 blocks per subscriber, block idle-timeout 50s.

config firewall ippool
    edit "CGN_pba_203.0.113.0"
        set type port-block-allocation
        set startip 203.0.113.0
        set endip 203.0.113.255
        set block-size 1888
        set num-blocks-per-user 4
        set pba-timeout 50
        set permit-any-host enable
    next
end

Same maths, different words: ~1888–2048 ports of headroom per subscriber, expandable to 3–4× under load. permit-any-host enable is FortiGate's equivalent of Huawei's full-cone — endpoint-independent mapping so P2P, gaming and VoIP survive the CGN. Note each node's pool is a different subnet — that's the source-hash model in the config.

Why block-size 1888? In FortiOS 7.4, kernel-NAT PBA uses a fixed port range of 5117–65533 (60,417 usable ports) that cannot be changed. 1888 is picked so exactly 32 blocks fit per public IP (1888 × 32 = 60,416) — i.e. 32 subscribers per public IP at one block each, with almost no wasted ports. num-blocks-per-user 4 then lets a busy subscriber expand on demand (up to 3 extra blocks); those heavier users draw down the 32, so the effective count falls as subscribers grow. We target the FortiOS 7.4 release from v7.4.8 (build 2795) onwards — earlier 7.4 builds were unstable with OSPF and BGP. Note the fixed 5117–65533 range is version-specific, so re-check it on any other release.

3 — Selecting subscribers & applying NAT

Huawei matches inside prefixes with an ACL and binds NAT to the ingress interface. FortiGate does it as a firewall policy — which is actually an advantage: NAT and security are the same object, and it's trivial to add no-CGN passthrough policies for IPTV, DNS or already-public customers above the CGN rule.

// Huawei
acl number 3501
 description GPON-FIBER-ACCESS
 rule 1000 permit ip source 100.112.0.0 0.0.255.255
#
interface 100GE0/4/0
 nat bind acl 3501 instance CGN precedence 0
// FortiGate
config firewall address
    edit "cgn-subscribers-01"
        set subnet 100.64.0.0 255.255.128.0
    next
end
config firewall policy
    edit 1
        set name "CGN_pba@isp"
        set srcintf "vlan.2608" "vlan.2610"
        set dstintf "vlan.2600" "vlan.2602"
        set srcaddr "cgn-subscribers-01"
        set dstaddr "all"
        set logtraffic-start enable
        set nat enable
        set ippool enable
        set poolname "CGN_pba_203.0.113.0" "CGN_pba_198.51.100.0"
        set global-label "CGN"
    next
end
On the FortiGate, ordering matters: put no-CGN policies (public passthrough, IPTV, DNS) above the catch-all CGN policy, or that traffic gets needlessly translated. On the Huawei you achieve the same with ACL precedence and by simply not binding those prefixes.

Recommended block / allow posture

A CGN sits directly between your subscribers and the internet, which makes it the right place to enforce a hygiene policy — both to protect subscribers and to protect your IP reputation, since everyone on a pool shares the same public addresses. On the FortiGate this is an ordered set of firewall policies above and around the CGN rule; on the Huawei you'd express the same with interface ACLs. The posture we deploy:

Block

  • Outbound SMTP (TCP/25), except sanctioned relays — the single most important rule. One compromised host behind CGN becomes a spam cannon and gets your shared public IPs blacklisted, hurting every subscriber on that pool. Permit only your own mail relays; block the rest.
  • NetBIOS (135–139) & SMB (445) — worm / ransomware lateral-movement ports (the Sasser / Nimda / WannaCry lineage). They have no business crossing the internet edge.
  • SSDP (1900) & other reflection/amplification sources — stop your subscribers being conscripted into DDoS reflection.
  • Inbound SOCKS (1080) — open-proxy abuse.
  • RIP (520) / any routing protocol from subscribers — nothing downstream should be injecting routes.
  • Bogons & martians, both directions — drop RFC 1918, reserved and unallocated source/destination addresses.
// Huawei — one ACL + traffic-filter

Deny the abused ports, permit SMTP only to your relays, then apply it inbound on the subscriber-facing interface:

acl number 3900
 description CGN-EDGE-HYGIENE
 rule 10   permit tcp destination 198.51.100.240 0.0.0.15 destination-port eq smtp
 rule 20   deny   tcp destination-port eq smtp        ! all other SMTP/25
 rule 30   deny   udp destination-port range 137 139  ! NetBIOS
 rule 31   deny   tcp destination-port eq 445          ! SMB
 rule 40   deny   udp destination-port eq 520          ! RIP
 rule 50   deny   tcp destination-port eq 1080         ! SOCKS
 rule 60   deny   udp destination-port eq 1900         ! SSDP
 rule 1000 permit ip
#
interface 100GE0/4/0
 traffic-filter inbound acl 3900

The reverse direction (inbound SOCKS, bogons from the internet) is a matching ACL on the public interface; source-spoofing is usually handled with ip urpf rather than an ACL.

// FortiGate — ordered policies

FortiOS defaults action to deny, so a rule with no set action accept blocks. dstaddr-negate flips "to the relays" into "to all except the relays". (EXCLUDE_smtp = your relays, BOGON-IPv4 = martian prefixes; subscriber side = vlan.2608/10, internet side = vlan.2600/02.)

config firewall policy
    edit 2                       ! SMTP: block to all EXCEPT relays
        set name "block_outgoing_SMTP"
        set srcaddr "cgn-subscribers-01"
        set dstaddr "EXCLUDE_smtp"
        set dstaddr-negate enable
        set service "SMTP"
    next
    edit 3
        set name "block_NETBIOS"
        set service "NETBIOS"
    next
    edit 4
        set name "block_SMB"     ! Sasser/Nimda/WannaCry
        set service "SMB"
    next
    edit 5
        set name "block_RIP"
        set service "RIP"
    next
    edit 6                       ! internet -> subscriber
        set name "block_incoming_SOCKS"
        set service "SOCKS"
    next
    edit 7
        set name "block_SSDP"    ! DDoS reflection
        set service "SSDP"
    next
    edit 13
        set name "block_BOGON-out"
        set srcaddr "cgn-subscribers-01"
        set dstaddr "BOGON-IPv4"
    next
    edit 17
        set name "block_BOGON-in"
        set srcaddr "BOGON-IPv4"
        set dstaddr "cgn-subscribers-01"
    next
end

Allow (and what to keep off the CGN)

  • DNS to your resolvers — rate-limited per subscriber — allow it, but cap sessions so a compromised host can't run DNS amplification or hammer the resolvers.
  • IPTV / managed multicast — routed straight through, no CGN.
  • CPE management, RADIUS/RSSO accounting, loopback management — the control-plane bits the access network needs.
  • Public-IP customers — passthrough without translation (these policies sit above the CGN rule).
  • An overall per-subscriber session cap — the backstop from section 4, applied as a shaper on the catch-all.
// Huawei — don't bind, then CAR

The elegant part: anything that must skip CGN simply isn't in the NAT bind ACL, so IPTV, DNS and public customers route straight through — no explicit "allow" needed. DNS abuse is rate-limited with CAR:

! IPTV / DNS / public prefixes are NOT in acl 3501/3502,
! so they are never translated — no rule required.
#
! rate-limit DNS bandwidth per subscriber (CAR)
acl number 3910
 rule 5 permit udp destination-port eq dns
traffic classifier DNS
 if-match acl 3910
traffic behavior DNS-CAR
 car cir 2000 pir 4000 green pass red discard
traffic policy SUB-EDGE
 classifier DNS behavior DNS-CAR
interface 100GE0/4/0
 traffic-policy SUB-EDGE inbound

Per-subscriber session caps come from the NAT session-limit knobs in section 4, not a shaper.

// FortiGate — policies above the CGN rule
config firewall policy       ! all ABOVE the CGN catch-all
    edit 8
        set name "allow_RADIUS_RSSO"
        set action accept
        set srcaddr "rsso-proxy"
        set dstaddr "lo2"
        set service "RADIUS" "ALL_ICMP"
    next
    edit 9
        set name "allow_IPTV"       ! subs <-> iptv-core, no CGN
        set action accept
        set srcaddr "cgn-subscribers-01"
        set dstaddr "iptv-core-01" "iptv-core-02"
        set service "ALL"
    next
    edit 10
        set name "allow_DNS"        ! subs -> resolvers
        set action accept
        set srcaddr "cgn-subscribers-01"
        set dstaddr "dns-resolvers"
        set service "DNS" "ALL_ICMP"
    next
    edit 19
        set name "allow_to_CPE_MGMT"
        set action accept
        set srcaddr "mgmt-jumphost"
        set dstaddr "cgn-subscribers-01"
        set service "HTTP" "HTTPS" "SSH"
    next
    ! + reverse-path allows (allow_from_IPTV / _DNS)
    ! + allow_LOOPBACK_MGMT
end
#
config firewall shaping-policy   ! per-subscriber caps (section 4 shapers)
    edit 3
        set name "outgoing-DNS_max_limit_2048"
        set service "DNS"
        set per-ip-shaper "conn_limit_2048-total"
    next
    edit 2
        set name "outgoing-max_limit_7552"
        set service "ALL"
        set per-ip-shaper "conn_limit_7552"
    next
end
Full picture, top to bottom: allow / no-CGN (RADIUS, IPTV, DNS, CPE & loopback management) → block (SMTP, NetBIOS, SMB, RIP, SOCKS, SSDP, bogons) → the catch-all CGN translation → shaping / CAR caps. On the FortiGate that's ordered firewall policies; on Huawei it's ACLs + traffic-filter plus simply not binding NAT to what shouldn't be translated.

4 — Per-subscriber session limits

A CGN's worst enemy is one compromised host opening hundreds of thousands of sessions. Both platforms cap sessions per subscriber; FortiGate borrows its traffic shaper to do it.

// Huawei
nat instance CGN id 1
 nat session-limit total 12000
 nat session-limit udp 8000
 nat reverse-session-limit total 20000
// FortiGate (per-ip-shaper)
config firewall shaper per-ip-shaper
    edit "conn_limit_12k"
        set max-concurrent-session 12288
    next
    edit "nat_pba_limit_4k"
        set max-concurrent-session 7552
        set max-concurrent-udp-session 4096
    next
end
! then: set per-ip-shaper "conn_limit_12k" on the policy

5 — Session aging

// Huawei
nat instance CGN id 1
 nat session aging-time tcp  600
 nat session aging-time udp  300
 nat session aging-time icmp 5
// FortiGate
config system session-ttl
    config port
        edit 2
            set protocol 17          ! UDP
            set timeout 60
            set refresh-direction outgoing
        next
        edit 3
            set protocol 1           ! ICMP
            set timeout 2
        next
    end
end

6 — Routing: OSPF for infra, BGP for policy

The routing design is deliberate and identical on both boxes — and it's what makes the two-box source-hash model clean:

  • OSPF carries only point-to-point links and loopbacks. Nothing else. Its only job is to make BGP next-hops and BFD reachable — a tiny, stable IGP that almost never changes. Every uplink is network-type point-to-point.
  • BGP carries the real traffic: the default route inbound (so translated subscribers reach the internet) and each node's public pools outbound (so return traffic lands on the right box). Pools are originated from a blackhole/NULL0 route and redistributed — advertise the aggregate, discard the unused.
// Huawei
! OSPF: p2p links + loopback only
ospf 1
 area 0.0.0.0
  network 169.254.0.10 0.0.0.1     ! /31 uplink
  network 176.61.156.4 0.0.0.0     ! loopback
!
! BGP: pool out (blackhole), default in
ip route-static 203.0.113.0 255.255.255.0 NULL0
bgp 64500
 ipv4-family unicast
  import-route static route-policy NO-INSIDE-NAT-EXPORT
  maximum load-balancing ebgp 8
// FortiGate
config router ospf                  ! p2p + loopback only
    config ospf-interface
        edit "vlan.2608"
            set network-type point-to-point
            set bfd enable
    end
    config network
        edit 5
            set prefix 10.80.245.251 255.255.255.255   ! loopback
    end
end
config router static
    edit 1
        set dst 203.0.113.0 255.255.255.0
        set blackhole enable
    next
end
config router bgp
    set as 64500
    set ebgp-multipath enable
    set ibgp-multipath enable
    config neighbor
        edit "10.80.248.1"
            set update-source "vlan.2600"
            set prefix-list-in "default_route"
            set prefix-list-out "cgn_public_prefixes"
            set remote-as 64500
            set password ENC <redacted>
        next
    end
    config redistribute "static"
        set status enable
    end
end

Note the FortiGate neighbours: prefix-list-in "default_route" and prefix-list-out "cgn_public_prefixes" — BGP takes in a default and puts out only the pools, exactly the division above. Keeping the IGP to infra-only means a subnet or pool change never perturbs OSPF, and troubleshooting is a two-layer problem instead of one tangled mess.

7 — Logging & traceability

For a CGN this is not optional: when abuse or a lawful request arrives, you must be able to answer "who held this public IP:port at this timestamp". Both stream port-block allocation events to a collector. This is also where regulatory data-retention and lawful-intercept obligations get fed — how you retain, secure and serve those records is a subject in its own right, and one we'll cover in a separate deep dive rather than here.

// Huawei
nat instance CGN id 1
 nat log host 192.0.2.20 5514 source 203.0.113.4 5000
 nat log user enable
// FortiGate
config log syslogd3 setting
    set status enable
    set server "192.0.2.20"
    set port 5514
    set format csv
end
config log syslogd3 filter
    config free-style
        edit 100
            set category event
            set filter "logid 0100022015 0100022016"   ! CGN session start/close
        next
    end
end
Because each subscriber is pinned to one node by source hash, the port-block logs on that node are the complete story for that subscriber — you don't have to correlate across boxes. That traceability simplicity is a quiet bonus of the pure-L3 model.

Operating & verifying

The commands you live in day to day. Watch two numbers above all: per-pool port-block utilisation (a full pool means new subscribers can't get out), and per-subscriber session count against the limits (a spike is usually a compromised host, not growth).

// Huawei
display service-location 7
display nat instance CGN
display nat address-usage instance CGN address-group CGN-GPON-FIBER-ACCESS-01
display nat statistics session-usage
display nat session table slot 7
display service-ha global-information

address-usage needs both instance and address-group; the session table also accepts nat-instance CGN. Watch pool-usage % and session-usage for capacity.

// FortiGate
diagnose firewall ippool-all list
diagnose firewall ippool-all stats CGN_pba_203.0.113.0
diagnose firewall shaper per-ip-shaper list
diagnose sys session stat
get router info bgp summary
get router info ospf neighbor

Subscriber awareness & visibility (a FortiGate edge)

Operationally the FortiGate brings a lot of nice-to-haves a pure router doesn't — the other half of the "firewall, not just a router" story. Two we lean on:

RSSO subscriber awareness. We mirror RADIUS accounting from the BNG to the FortiGate (RADIUS Single Sign-On). The box then maps every session to a subscriber identity — a username — not just an inside 100.64/x address, so logs, troubleshooting and abuse response are per-subscriber. (The allow_RADIUS_RSSO policy from earlier is what lets those accounting copies land.)

config user radius
    edit "RSSO-isp-radius"
        set rsso enable
        set rsso-validate-request-secret enable
        set rsso-secret ENC <redacted>
        set rsso-endpoint-attribute User-Name
        set sso-attribute NAS-Identifier
        set rsso-flush-ip-session enable
        set rsso-ep-one-ip-only enable
    next
end

Per-subscriber port-block visibility. Every port-block allocation is logged with the subscriber and protocol, and RSSO ties it to a username — so you can see exactly how many TCP and UDP blocks each subscriber is holding. That turns "the pool is filling up" into "these specific subscribers are near port exhaustion", and lets you flag the heavy hitters who'd be better off on a dedicated public IP (1:1) — a targeted fix that improves their experience without growing the whole pool.

Huawei exposes per-user data too (display nat user-information), but tying it to subscriber identity and this depth of per-user, per-protocol port-block tracking is where the FortiGate's firewall heritage pays off operationally.

Gotchas

  • Size the log pipeline, not just the pool. Port-block logging is light per event but real volume at scale — make sure the collector and its path keep up, or you lose the traceability the whole CGN depends on.
  • Watch port-block exhaustion. Monitor pool utilisation and tune block size / blocks-per-user (and Huawei's extended-port ratio) to your subscriber base; on FortiGate pba-timeout governs how fast idle blocks are reclaimed.
  • Aging vs table pressure. Too-low UDP/TCP aging churns long-lived sessions (VPNs, IoT keep-alives); too-high fills the table with idle sessions.
  • Endpoint-independent mapping is a trade. full-cone / permit-any-host fixes P2P and VoIP but is permissive — always pair it with the per-subscriber session limits.
  • Policy order (FortiGate). Keep no-CGN passthrough policies above the catch-all CGN rule; on Huawei use ACL precedence and selective binding.
  • FortiOS version (FortiGate). Target the 7.4 train from v7.4.8 (build 2795) onwards — earlier 7.4 builds were unstable with OSPF and BGP in our production experience. On a routing-critical CGN edge, the code train matters as much as the config.

Licensing & scale ceilings

For "cost-effective", licensing is half the story — and it's where the two diverge sharply.

// Huawei — licensed on two axes

NAT is gated by licence, per service board:

  • Throughput — bought in Gbps tiers, up to ~90 Gbps per board.
  • Session scale — bought in millions of concurrent sessions, up to ~32 million.
license
 active nat bandwidth-enhance 60 slot 7   ! Gbps throughput tier
 active nat session-table size 3  slot 7   ! session-scale tier
 active nat bandwidth-enhance 60 slot 8    ! license the standby board
 active nat session-table size 3  slot 8   ! identically

You size — and pay for — the tier you need. Because licensing is per board, the standby VSUP must be licensed identically (or it fails over cold) — so a 1+1 board pair doubles the CGN licence cost.

// FortiGate — no throughput licence

The NP7 does NAT at hardware line rate with no per-Gbps or per-session unlock — you get the box's full capacity out of the box. There's simply no throughput licence to buy.

The only CGN-related licence sits on the larger models with hardware CGN (hyperscale), which need a hyperscale firewall licence to enable that mode. The 1000F itself needs none for the deployment shown here.

Practically: the FortiGate's ceiling is its hardware; the Huawei's ceiling is whatever tier you've licensed, up to the board maximum. For a growing ISP that's the difference between "upgrade the box" and "buy a bigger licence".

Which platform — economics, not scale

Raw scale rarely decides it: both the FortiGate 1000F and the NE8000 + VSUP handle millions of concurrent sessions. The real choice is about cost shape, operations, and taste. Weigh:

  • Do you want a firewall too? The FortiGate is a firewall doing CGN — stateful security, IPS, DPI-based traffic shaping, RSSO subscriber awareness and per-subscriber port-block visibility, all in one box. The Huawei is a pure router; you'd add separate kit for those. If the nice-to-haves matter, that alone can decide it.
  • Regulatory & vendor policy. Hardware price isn't the split — Huawei is often aggressively priced. What can decide it is policy: a growing number of countries restrict or forbid Huawei in ISP core networks, which can take it off the table regardless of technical merit.
  • Licensing vs support cost. The 1000F has no throughput or session licence (only a hyperscale licence on the larger hardware-CGN models). Huawei licenses per board on throughput and sessions — so a 1+1 board pair doubles the CGN licence spend. Weigh both against each vendor's support-contract pricing.
  • Know-how. Running a platform your team already knows — one vendor, one toolchain — often beats any spec-sheet edge. A second platform is a second set of skills to hire for and keep current.
  • Existing estate & taste. Already run a Huawei carrier core (where permitted)? The NE8000 slots into your operational model. Beyond that, some of it is simply which platform you'd rather live with.

Either way the fundamentals are identical: distinct public subnet per node, source-hash ECMP, port-block allocation, per-subscriber limits, OSPF-for-infra / BGP-for-pools, and airtight logging. Get those right and the box underneath is a preference and economics call — not a correctness one.

// disclaimer This deep-dive series is drawn from our production experience. It's here to fix the common mistakes and help you deploy things properly — not a guarantee it fits every network. Validate against your own design, scale and current vendor releases before applying anything.

Designing or scaling CGN for your ISP and need help? Get in touch.