I ran into this exact scenario and found myself severely disappointed in the lack of efficiency in the Juniper PBA allocation method. According to one of our Sales Engineers with Juniper, the behavior is intended for security purposes to help prevent again DDOS attacks by making it harder to predict what block of ports belong to a subscriber. For my money, I wish this behavior could be altered so that they are forced to use all ports in their currently allocated blocks before being given more blocks. I haven't found a solution to this per se, but I had to get creative to find a way to make it more efficient. What I have done makes for a more complex configuration, but has undoubtedly made PBA more efficient for us, so I wanted to share on this thread for anyone else who may want to experiment with it. I have broken my PBA pools in two and am now using one as a "burst" pool. I termed them "primary" and "overflow" to match Juniper verbiage. The burst pool (or overflow) is referenced by the primary pool for use only when a subscriber has exceeded all their blocks and ports from the primary pool, at which point they can receive more blocks from the burst pool. Once they are in the burst pool however, the waste can occur again as you have described, but it is much harder for subscribers to obtain unneeded blocks given they must exhaust the ports from the primary blocks first. I will show a config snippet of mine to help illustrate the point. As an aside, I don't know who people get away with giving customers only like 1000 ports... We run dual stack and still have had complaints when we were using 2016 per sub, now we have it so they can use a maximum of 4032. Here is the config reference:
MX-1-re0# show services nat source pool XXXXFORD-PUBLIC-NAT-POOL-A-PRIMARY
address {
XXX.XXX.XX6.0/32 to XXX.XXX.XX6.255/32;
}
port {
automatic {
random-allocation;
}
block-allocation {
block-size 252;
maximum-blocks-per-host 4;
interim-logging-interval 86400;
}
}
burst-pool XXXXFORD-PUBLIC-NAT-POOL-A-OVERFLOW;
mapping-timeout 120;
MX-1-re0# show services nat source pool XXXXFORD-PUBLIC-NAT-POOL-A-OVERFLOW
address {
XXX.XXX.XX6.0/32 to XXX.XXX.XX6.255/32;
}
port {
automatic {
random-allocation;
}
block-allocation {
block-size 252;
maximum-blocks-per-host 12;
interim-logging-interval 86400;
}
}
mapping-timeout 120;
MX-1-re0# show services nat source rule-set SOURCE-NAT-RULES rule XXXXFORD-NAT-RULE
match {
source-address-name XXXXFORD-PRIVATE-INET-NAT-AS;
}
then {
source-nat {
pool {
XXXXFORD-PUBLIC-NAT-POOL-A-PRIMARY;
}
mapping-type endpoint-independent address-pooling-paired;
filtering-type {
endpoint-independent;
}
}
}
I use the same IP range for the Primary and Burst pools so that subscribers receive a consistent external IP address when they need to use the overflow pool. The primary pool initially allows subs to use 4 blocks for a total of 1008 ports, which also means they must use all 1008 ports before getting access to more blocks in the burst pool which has significantly reduced the average amount of blocks used per sub. There are of course may combinations of blocks and block sizes that may yield better results, but overall I am happy with what I have currently. The only weird thing I have noticed is that once a subscriber is given a block or blocks from the overflow pool, they will hold at least 1 block from the overflow pool until all their blocks have been released from the primary pool. This leaves most of our subscribers with 1 block given to them from the overflow pool at all times. Annoying, but still much more tolerable than the waste you've already highlighted. This is what it looks like for most of our subs:
MX-1-re0> show services nat source port-block host-ip 100.XX.XX.245
Interface: mams-3/0/0
Pool name: XXXX-PUBLIC-NAT-POOL-A-PRIMARY
Port-overloading-factor: 1 Port block size: 252
Max port blocks per host: 4 Port block active timeout: 0
Used/total port blocks per host: 2/4
Host_IP External_IP Port_Block Ports_Used/ Block_State/
Range Ports_Total Left_Time(s)
100.XX.XX.245 XXX.XXX.XXX.8 27988-28239 2/252*1 Inactive/-
100.XX.XX.245 XXX.XXX.XXX.8 33784-34035 41/252*1 Active/-
Interface: mams-3/0/0
Pool name: XXXX-PUBLIC-NAT-POOL-A-OVERFLOW
Port-overloading-factor: 1 Port block size: 252
Max port blocks per host: 12 Port block active timeout: 0
Used/total port blocks per host: 1/12
Host_IP External_IP Port_Block Ports_Used/ Block_State/
Range Ports_Total Left_Time(s)
100.XX.XX.245 XXX.XXX.XXX.8 40840-41091 0/252*1 Query/-
MX-1-re0> show services nat source port-block host-ip 100.XX.XX.149
Interface: mams-3/0/0
Pool name: XXXX-PUBLIC-NAT-POOL-A-PRIMARY
Port-overloading-factor: 1 Port block size: 252
Max port blocks per host: 4 Port block active timeout: 0
Used/total port blocks per host: 4/4
Host_IP External_IP Port_Block Ports_Used/ Block_State/
Range Ports_Total Left_Time(s)
100.XX.XX.149 XXX.XXX.XXX.1 7072-7323 3/252*1 Active/-
100.XX.XX.149 XXX.XXX.XXX.1 27484-27735 2/252*1 Inactive/-
100.XX.XX.149 XXX.XXX.XXX.1 30004-30255 1/252*1 Inactive/-
100.XX.XX.149 XXX.XXX.XXX.1 34792-35043 2/252*1 Inactive/-
Interface: mams-3/0/0
Pool name: XXXX-PUBLIC-NAT-POOL-A-OVERFLOW
Port-overloading-factor: 1 Port block size: 252
Max port blocks per host: 12 Port block active timeout: 0
Used/total port blocks per host: 1/12
Host_IP External_IP Port_Block Ports_Used/ Block_State/
Range Ports_Total Left_Time(s)
100.XX.XX.149 XXX.XXX.XXX.1 35548-35799 0/252*1 Query/-
I know it still isn't maximized efficiency, but from my testing it has proved much better than regular PBA.
------------------------------
SETH WAGONER
------------------------------
Original Message:
Sent: 12-26-2025 17:03
From: JONATHAN LEWIS
Subject: Juniper CGNAT not releasing sessions on port-block "Zombie sessions"
As a further update on this, after some more research, I've discovered the Juniper PBA algorithm "is stupid" in that when the active port block runs out of ports, rather than look at the other port blocks assigned to a subscriber IP, it simply allocates a new block (if it can). The end result is if customers use more ports with any frequency than your block size, you will end up with many customers having their max port blocks number of blocks, all but the active being very sparsely utilized. i.e.
Used/total port blocks per host: 9/9
Host_IP External_IP Port_Block Ports_Used/ Block_State/
Range Ports_Total Left_Time(s)
100.64.82.20 xxx.yyy.8.0 2048-2303 4/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 9472-9727 4/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 11264-11519 3/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 14848-15103 2/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 16128-16383 127/256*1 Active/-
100.64.82.20 xxx.yyy.8.0 19968-20223 4/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 30208-30463 2/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 46848-47103 2/256*1 Inactive/-
100.64.82.20 xxx.yyy.8.0 48384-48639 3/256*1 Inactive/-
This customer has used all their permitted port blocks...but they're not exactly "using" most of them. They're essentially hogging port-blocks that should never have been allocated, keeping those resources from being usable by other customers. There doesn't seem to be a knob to tune this behavior other than perhaps designing your PBA config in such a way that most customers never need to allocate a second port block...but that would also be inefficient due to having to size the port-block such that most customers never get near a single port block's capacity. This effectively makes oversubscription of your CGNAT external pools "dangerous".
------------------------------
JLewis
Original Message:
Sent: 11-24-2025 10:08
From: JONATHAN LEWIS
Subject: Juniper CGNAT not releasing sessions on port-block "Zombie sessions"
I'm currently investigating the same issue, and found your post. Quite some time ago, JTAC had us change our PBA active-block-timeout to 0, but I still see many customer IPs sparsely using most or all of their possible port-blocks, much like the above. This makes me worry that oversubscription of the CGNAT external pools may run into issues with there being plenty of unused ports per IP, but few, if any, available port-blocks. Did you find a solution to this issue?
------------------------------
JONATHAN LEWIS
Original Message:
Sent: 05-23-2023 22:38
From: jeep1941
Subject: Juniper CGNAT not releasing sessions on port-block "Zombie sessions"
We are using Juniper MX480s with SPC3 cards for CGNAT. Currently we have TCP and UDP ports with specific inactivity-timeouts, all working well as attended. A port block would be allocated and when the active-block-timeout expires would allocate a new port block and new sessions would use the new block and the sessions that are still open would remain on the old port block. All good and well, but we require the session to eventually also be moved over to the new port block or closed, as the session would keep this port block open and never be released. F5 CGNAT routers call this zombie sessions .
"A zombie port block, which is a port block that has reached the Block Lifetime limit but cannot be released due to active connections, is released when all active connections become inactive, or when the Zombie Timeout value is reached."
Would Juniper have a way in which these "Zombie" sessions be released/closed or moved to new allocated port block. Similar to the F5 to timeout a "Zombie" session, or alternatively running a specific command or script?
Example of a user which have many "Zombie" sessions.
Interface: mams-1/0/0
Pool name: NAT-POOL-1
Port-overloading-factor: 1 Port block size: 128
Max port blocks per host: 12 Port block active timeout: 930
Used/total port blocks per host: 7/12
Host_IP External_IP Port_Block Ports_Used/ Block_State/
Range Ports_Total Left_Time(s)
1.2.3.4 5.6.7.8 4224-4351 3/128*1 Inactive/-
1.2.34 5.6.7.8 4992-5119 2/128*1 Inactive/-
1.2.3.4 5.6.7.8 18816-18943 22/128*1 Active/495
1.2.3.4 5.6.7.8 31232-31359 1/128*1 Inactive/-
1.2.3.4 5.6.7.8 39424-39551 3/128*1 Inactive/-
1.2.3.4 5.6.7.8 48640-48767 1/128*1 Inactive/-
1.2.3.4 5.6.7.8 59776-59903 1/128*1 Inactive/-
Would appreciate your assistance.
------------------------------
Christiaan
------------------------------