QuantumBNG 1.4.0 — Validation Report, 5,000 Subscribers

Release: QuantumTouch BNG 1.4.0, installer bundle 1.4.0-13
Run: 699, 2026-10-12, 188 minutes
Platform: 2-socket Intel Xeon E5-2640 v2 (2013 silicon), Ubuntu 24.04.4
Result: 4,663 checks passed, 1 failed, 4 skipped


1. Summary

A single 188-minute run of the full release suite against one physical BNG with
two 40GbE dataplane ports and two load-generator hosts, at a working set of
5,000 concurrent subscribers.

Subscribers onboarded 5,000 of 5,000 — 100%
Onboarding rate 398.7 per second, 5,000 bound in 12.5 s
In-rush completion 391.8/s cold, 405.5/s warm
Downstream throughput 11.7 Gbps mean, 16.5 Gbps peak, 30.2 GB moved
QoS compliance 480 of 480 measured subscribers within contract
Churn integrity 1.0 — zero leaked sessions, zero orphans
Forwarding (2 workers, 64B) 13.57 Mpps, platform-limited
Per-packet cost ~330 cycles at 64B
Checks passed 4,663 of 4,668

Read the hardware line before any throughput figure. These numbers come
from a processor that launched in 2013. They are floors, not best-case
results — see §3.1.

Four of the twenty forwarding cells are maximum-throughput numbers. A
further thirteen are wire-speed results — the box forwarded everything offered,
at 90% of link, with zero loss — and three are floors where the rig never filled
the link. Every cell is labelled with which it is. §5.7 explains how to read
them, and no floor is promoted into a product claim anywhere in this report.


2. How to read this report

Every performance measurement carries a limited_by field recording what ran
out first
. Without it a reader cannot tell a product measurement from a
measurement of our test rig.

limited_by Means Treat as a product figure?
bng The platform was the constraint Yes
generator Our load generators ran out first It depends — see below
rss Receive-side distribution capped it No — RSS is the limit
none Neither side saturated No — a latency or shape result
controller The controller was the constraint Yes, but name the component
serial-by-design One operation in flight on purpose No — latency, not throughput

A generator label alone is not enough to judge a forwarding cell, because it
covers two different situations. If the generators had the link essentially full
and the box forwarded everything with zero loss, that is a wire-speed result
and a legitimate product figure. If the generators never filled the link, the
cell is a floor that understates the platform. The forwarding matrix in §5.7
separates these explicitly and this report never promotes a floor into a claim.

Two further conventions used throughout:

  • offered_headroom > 1 means work arrived faster than the box drained it,
    so the measured rate is the box's own. Below 1, the figure measures our
    request rather than the platform.
  • completion_rate is preferred over mean_rate. Mean rate divides by
    wall clock including ramp, and understates the platform.

A skip is not a pass. Each of the four is named in §6.2 with its reason.


3. Test environment

3.1 Platform under test — hardware

Server 2-socket Intel Xeon E5-2640 v2 @ 2.00 GHz
Cores / threads 16 cores (2 × 8), 1 thread per core — SMT disabled
Cache / NUMA 40 MiB L3 (2 instances), 2 NUMA nodes
Memory 31 GiB, with 8 × 1 GiB hugepages (all allocated to the dataplane)
Dataplane NICs 2 × Intel XL710 40GbE backplane, [8086:1580] rev 01
NIC driver vfio-pci, both devices on NUMA node 0
OS / kernel Ubuntu 24.04.4 LTS, kernel 6.8.0-146-generic

Full network controller inventory, read from dpdk-devbind --status on the
box under test. The XL710s are the only ports that carry subscriber traffic;
the rest are management and are listed so the inventory is complete:

PCI Controller chip Interface Driver Role
0000:01:00.0 Intel XL710 40GbE backplane [8086:1580] FortyGigabitEthernet1/0/0 vfio-pci Access — subscriber side
0000:47:00.0 Intel XL710 40GbE backplane [8086:1580] FortyGigabitEthernet47/0/0 vfio-pci Uplink — core side
0000:04:00.1 Intel DH8900CC Gigabit backplane [8086:043c] enp4s0f1 igb Management / harness SSH (the only active 1G port)
0000:04:00.2-4 Intel DH8900CC Gigabit backplane [8086:043c] enp4s0f2-4 igb Unused
0000:4a:00.0 Intel I350 Gigabit [8086:1521] enp74s0f0 igb Unused on this box
0000:4a:00.1 Intel I350 Gigabit [8086:1521] enp74s0f1 igb Unused
0000:04:00.0 Intel DH89XXCC QAT [8086:0434] — — Crypto accelerator, unused

Both XL710 ports are backplane variants — this is a blade chassis, not a
box with pluggable optics. Neither the QAT accelerator nor any offload beyond
RSS is used on the forwarding path; no-tx-checksum-offload is set in §3.3, so
the per-packet cost in §5.7 is software forwarding cost.

What this hardware means for the numbers

The processor launched in 2013. The E5-2640 v2 is a 2.0 GHz Ivy Bridge-EP
part — two generations before DDR4, and roughly a decade behind anything a
customer would deploy new today. We state the platform prominently because it is
the most important context for every forwarding figure here: these are floors
measured on old silicon
. A reader deploying current hardware can reason
upward from them.

Symmetric multithreading is disabled (1 thread per core), so each dataplane
worker is a physical core with no sibling contention — and equally no benefit
from SMT.

Core isolation is applied. The boot command line is:

default_hugepagesz=1G hugepagesz=1G hugepages=8 irqaffinity=0-1
isolcpus=2-6 nohz_full=2-6 rcu_nocbs=2-6
intel_pstate=disable processor.max_cstate=1 intel_idle.max_cstate=0 idle=poll
numa_balancing=disable hpet=disable mce=off tsc=reliable nmi_watchdog=0
intel_iommu=on iommu=pt

The kernel confirms it: /sys/devices/system/cpu/isolated reads 2-6, and all
five VPP workers sit on exactly that set with interrupts confined to cores 0–1.

An honest note on what isolation bought. We measured this platform both
without and with core isolation. It produced no throughput improvement. The
two isolated runs reproduce each other to within 0.2% on the single-worker
cells, and both sit 2.5–3.9% below the single un-isolated run; the two-worker
cells agree within 1.1% across all three. We are not claiming isolation harms
forwarding — the un-isolated condition was measured once, and nohz_full /
rcu_nocbs carry their own overheads we did not isolate. The honest conclusion
is that on a box where nothing else contends for the worker cores, isolation
removes contention that was not there. It remains correct practice because it
protects against future contention, not because it raised these figures.

NUMA locality is correct. The dataplane uses main-core 1 with
corelist-workers 2-6, and CPUs 0–6 all sit on NUMA node 0 — the same node as
both NICs. No cross-socket traffic on the forwarding path.

3.2 Platform under test — software

Installer bundle quantumtouch-installer-1.4.0-13
BNG plugin 0.10.36, commit 9a12835, clean tree
Controller quantum-bng-controller:1.4.0 (container)
quantum_core v1.6.0
VPP v25.10.0-23~g744d3c715
CPESIM 0.4.17
Metrics Prometheus (container)

3.3 Dataplane configuration

Taken verbatim from the running /etc/vpp/startup.conf:

cpu     { main-core 1   corelist-workers 2-6 }
memory  { main-heap-size 2G }
buffers { buffers-per-numa 1048576   default data-size 2048 }
statseg { size 1024M }
dpdk    { uio-driver vfio-pci   no-multi-seg   no-tx-checksum-offload
          dev 0000:01:00.0 { num-rx-queues 5  num-tx-queues 6
                             num-rx-desc 4096 num-tx-desc 4096
                             rss { ipv4 ipv4-udp ipv4-tcp } }
          dev 0000:47:00.0 { ... same ... } }

Receive-queue placement — one queue per worker, both interfaces, no sharing:

Worker lcore Access queue Uplink queue
vpp_wk_0 2 0 0
vpp_wk_1 3 1 1
vpp_wk_2 4 2 2
vpp_wk_3 5 3 3
vpp_wk_4 6 4 4

The statistics segment is sized to 1024 MB. VPP's 32 MB default is exhausted
well before this scale, because the BNG allocates a forwarding entry and
per-session counters for every subscriber.

3.4 Service configuration

Subscriber pool 100.64.0.0/17 (32,766 usable)
Access interface FortyGigabitEthernet1/0/0
Uplink interface FortyGigabitEthernet47/0/0
AAA mode open admission (local AAA measured separately in §5.8)
Captive portal 100.64.0.1
Plan under test Bronze — 20/25 Mbps down (CIR/PIR), 10/12 Mbps up
DHCP relay giaddr block of 64, 100.64.127.1 upward

3.5 Licence state, verified at launch

The session cap is a property of the licence, not the platform, so the gate
records it before the run starts:

licence VALID        grant bng.max_sessions = 25000
cap 25000            0 sessions resident

A reader may notice the platform reports max_source: free_baseline even when
fully licensed. The number is correct; that provenance label is wrong
upstream
and is being tracked. The cap in force during this run was 25,000,
and the working set was 5,000 — so no result here is cap-constrained.

3.6 Load generation

Generators 2 hosts (bluedot2, bluedot3)
Simulator CPESIM 0.4.17

Both generator hosts are wired symmetrically with a port on each side of the
BNG, and each carries two roles:

Host Role Interface PCI bus Address
bluedot2 CPE generator (access side) enp1s0f0 01:00.0 L2 only, no address
bluedot2 Uplink origin (core side) enp71s0f0 47:00.0 10.0.0.3/24
bluedot3 CPE generator (access side) enp1s0f0 01:00.0 L2 only, no address
bluedot3 Uplink origin (core side) enp71s0f0 47:00.0 10.0.0.2/24

Both origins hold a return route 100.64.0.0/17 via 10.0.0.1 so downstream
traffic reaches the subscriber pool. This run used both hosts for CPE
generation, with bluedot3 as the active uplink origin (10.0.0.2).

The access-side interfaces carry no IP address: the simulator drives raw frames
on them, relaying DHCP with a source giaddr, so there is nothing for the host
kernel to address. The PCI buses above are derived from the predictable
interface names (enp71s0f0 → bus 0x47) and place the generator ports in the
same slots as the platform's own, these being identically-specified chassis.

On the generator NIC chips. The generators were not read with
dpdk-devbind during this run, so their controllers are not confirmed
first-hand here. The evidence that they match the platform is strong but
indirect: all three chassis enumerate the same PCI topology — a 40GbE port at
bus 01:00.0, a second at 0x47, and an Intel I350 pair at bus 0x4a (idle on
the box under test, and the management port on bluedot3). Identical
slot-for-slot enumeration across three machines indicates identically-specified
chassis,
which would put Intel XL710 backplane controllers in the generators too.
Treated as inference, not measurement, and flagged for first-hand confirmation
on the next rig session.

It does not weaken the report, because generator capability here is established
by measurement rather than part number: the ceilings in §5.7 are ~20 Gbps
aggregate, 15.2 Mpps at 128B and 21.93 Mpps at 64B
, each held byte-identical
across five different platform configurations. A part number would explain those
ceilings; it would not change them, and it is the measured ceiling that makes
the generator-limited cells floors.

The generators bound most forwarding cells, but not all in the same way.
Of the 20 UDP cells, 4 found a platform ceiling, 13 reached line rate with zero
loss, and 3 are floors where the rig under-offered. The distinction matters:
only the floors understate the platform. §5.7 separates them, and the rig's
~20 Gbps aggregate ceiling is what makes the large-frame cells wire-speed
results rather than shortfalls.


4. Method

4.1 Suite composition

Twelve suites, 4,668 checks: framework authentication, console dashboard,
security, capacity and cap enforcement, licence API, console UI behaviour, gated
local-AAA admission, simulated CPE (onboarding, traffic, QoS, churn), DORA
in-rush, dataplane, forwarding performance, and unauthenticated admission.

4.2 Pre-flight gate

The run does not start until twelve conditions hold. This is quoted verbatim
from the launch output because reproducibility depends on it:

===== 1/3 licence unit =====
  License state: VALID  (valid: true, 36500 days remaining)
===== 2/3 drain sessions to zero =====
  start: 0        final: 0
===== 3/3 launch gate =====
  OK   licence VALID          OK   grant 25000
  OK   cap 25000              OK   0 sessions
  OK   vpp connected          OK   status ok
  OK   aaa open               OK   Bronze shaped
  OK   pool /17               OK   captive portal set
  OK   access iface           OK   uplink iface
GATE: PASS
  BUNDLE_VERSION=1.4.0-13
  sha=9a12835 dirty=False

Two of these deserve explanation. Sessions are drained to zero explicitly
because a reboot does not clear them — the controller persists sessions and
restores them with their original timestamps, so a previous run's fleet would
silently become this run's starting condition. And the Bronze plan's rates are
verified
because a cancelled run can leave the plan unshaped, which would make
every QoS result in §5.6 meaningless while still looking plausible.

4.3 Parameters

Subscribers 5,000
Requested onboarding rate 200/s (900/s for the in-rush phases)
Success threshold 0.95
Churn duration 300 s
Churn fraction 0.5
Traffic direction bidirectional

4.4 Reproducibility

The platform ran bundle 1.4.0-13 from commit 9a12835 with a clean working
tree. Run 699 used identical parameters to runs 696 and 698 on the same
hardware, which is what makes the three-run comparison in §3.1 meaningful.


5. Results

5.1 Subscriber onboarding — run-699-cpe.json

What it measures. Every simulated CPE performs a real DHCP DORA exchange
through the relay. The check is how many reach a bound session, and how fast.

Why it matters. This is the operation an ISP performs most: a subscriber
powering on. A platform that onboards slowly recovers slowly from an outage.

Target / bound 5,000 / 5,000
Success rate 100%
Rate 398.7 bound per second
Elapsed 12.54 s
DORA latency p50 1,389 ms
DORA latency p95 9,279 ms
Minimum / maximum 211.7 ms / 10,514 ms
Retransmits 1,469

Read the latency figures as queueing delay, not service time. The whole
fleet is launched at once, so a subscriber's measured DORA time includes waiting
behind the rest of the storm. The p50 of 1.4 s does not mean a single
subscriber waits 1.4 s on an idle box — the minimum of 211 ms is closer to that.
The 1,469 retransmits are the DHCP protocol doing its job under a burst; no
subscriber was lost.

5.2 In-rush, cold versus warm — run-699-dora-inrush.json

What it measures. A 900/s requested storm against a cold box, then the same
storm again once caches and sessions are warm.

Why it matters. It separates steady-state capability from first-contact
behaviour — the difference between a planned migration and a recovery.

Phase Completion rate limited_by
Cold 391.8/s none
Warm 405.5/s none

In-rush: cold and warm phases, with the cold-versus-warm comparison

limited_by: none means neither the platform nor the generators saturated at
this offered level, so these are shape results rather than a ceiling. The warm
phase being ~3.5% faster than cold is the expected direction and small.

5.3 Capacity and cap enforcement

The licence granted bng.max_sessions = 25000; the working set was 5,000. The
capacity suite verifies the cap is read from the grant and enforced at its
boundary, with sessions beyond it moved to a captive state rather than dropped.
All capacity checks passed. No figure — this is a pass/fail surface, not a
measurement.

5.4 Churn and reconciliation — run-699-churn.json

What it measures. Half the fleet is released and re-established repeatedly
for 300 s, while client-side and controller-side session counts are compared.

Why it matters. Churn is normal ISP life — evenings, link flaps, CPE
reboots. The question is whether the platform's own view of its sessions stays
truthful under it.

Integrity 1.0
Converged yes, in 25.1 s
Maximum divergence 391 sessions
Leaked sessions none (empty list, enumerated)
Orphaned sessions none (empty list, enumerated)

Churn-longevity: connected sessions and controller/client divergence over the churn window

The leak and orphan results are enumerated, not derived. This matters: a
leak count computed as total − final_bound is an arithmetic identity and
proves nothing. These are empty lists of specific sessions, which is evidence.

5.5 Lease churn — run-699-leasechurn.json

What it measures. A cohort is held stable while another half churns around
it, checking the stable cohort is neither disturbed nor lost from the view.

Cohort size 2,500
Stable cohort 2,500
Stable sessions bound correct throughout
Stable sessions in controller view correct throughout
Lease renewal exercised no

Lease-time and partial-churn cohorts

Two caveats, both important. First, the stable cohort was genuinely 2,500 —
worth stating because a stable cohort of zero would make this check pass
while testing nothing. Second, lease_renewal_exercised is false: the lease
time was not shortened for this run, so no lease actually reached renewal inside
the window. The cohort's continuity is real; lease renewal under churn is not
covered by this run.

5.6 Traffic forwarding and QoS — run-699-cpe.json

What it measures. Two distinct workloads, which must not be conflated.
A bulk throughput phase drives downstream HTTP reads to find aggregate
capacity; a separate shaping phase drives per-subscriber traffic through the
policer and compares each subscriber's achieved rate against its plan.

Bulk throughput (downstream)

Downstream mean 11,710 Mbps (11.7 Gbps)
Downstream peak 16,505 Mbps (16.5 Gbps)
Transferred 30.17 GB in 20.6 s
Upstream negligible — this phase is a downstream-dominant HTTP read

Throughput over time, downstream and upstream

Per-subscriber shaping

480 subscribers were sampled and measured in both directions against the
Bronze plan (25 Mbps down / 12 Mbps up ceilings):

Within contract 480 of 480
Silent (moved no traffic) 0
Unresolved 0
Downstream median 22.14 Mbps against a 25 Mbps ceiling
Upstream median 10.93 Mbps against a 12 Mbps ceiling

QoS shaping — every measured subscriber against its plan ceiling

The silent: 0 and unresolved: 0 counts carry weight: a subscriber that moved
no traffic is reported in its own category rather than counted as compliant, so
480 of 480 is 480 real measurements rather than a pass inflated by silence.

Per-subscriber downstream rate, scatter
Per-subscriber downstream rate, distribution
Per-subscriber upstream rate, scatter
Per-subscriber upstream rate, distribution

The policer, actively marking

Both directions carried real load through the policer, and the colour split is
the result worth publishing because it shows the policer doing work rather than
merely passing traffic. Figures are the totals the panels themselves report.

Downstream — 32.90 GB over 23.5 s (≈11.2 Gbps mean, ~15 Gbps peak):

Colour Volume Share
Green (conforming) 23.964 GB 72.8%
Yellow (marked out-of-contract) 5.927 GB 18.0%
Red (dropped) 3.010 GB / 2,007,004 pkts 9.1%

Downstream policer bands — green conforming, yellow marked, red dropped

Upstream — 30.72 GB over 29.7 s (≈8.3 Gbps mean, ~12.3 Gbps peak):

Colour Volume Share
Green (conforming) 19.884 GB 64.7%
Yellow (remarked) 10.336 GB 33.6%
Red (dropped) 0.502 GB / 334,895 pkts 1.6%

Upstream policer bands — green conforming, yellow remarked, red dropped

Roughly a quarter of offered traffic downstream, and a third upstream, was
deliberately over-contract, and the policer classified every bit of it. That is
the enforcement path working end to end under multi-gigabit load in both
directions, not an idle-path check.

What this run does not establish. Neither direction is a search for a
forwarding ceiling — these are the shaping workload's offered rates, chosen to
exercise the policer. §5.7 is where forwarding limits are measured.

5.7 Forwarding performance and core scaling — run-699-coreperf.json

What it measures. A matrix of worker counts (1–5) against packet sizes
(64B, 128B, 512B, 1518B, plus a TCP profile), each cell recording throughput,
per-core efficiency, thread balance, and what limited it.

This is the section most easily misread, so start with the taxonomy. The
matrix covers 20 UDP cells — five worker counts against four frame sizes —
and sorts each into one of three kinds, which are not interchangeable:

Kind Cells What it means
BNG ceiling 4 the box dropped packets — a real forwarding limit
Line rate, zero loss 13 the box forwarded everything offered, at 90% of link, with zero loss
Floor — rig under-offered 3 the box kept up but the rig never filled the link

Performance scaling matrix, all 20 UDP cells

Only the 4 ceiling cells are maximum-throughput numbers. But the 13
line-rate cells are not merely "our generators ran out" either — they are
legitimate wire-speed results: the box forwarded every packet that reached it
while the link was essentially full. At 4 workers and 128 B or larger,
saturating this box would need 26 to 270 Gbps against the rig's 20, so those
cells are the strongest reading this rig can produce rather than a shortfall.
Only the 3 floor cells are genuine under-offers.

The run's JSON additionally carries 4 TCP-profile cells, which the matrix does
not render; all four are generator-limited and no figure in this report is drawn
from them.

Only these four cells measure the platform:

Workers Packet size Throughput Per core Cycles/packet
1 64B 6.78 Mpps 3.25 Gbps ~330
1 128B 6.17 Mpps 6.12 Gbps ~344
2 64B 13.57 Mpps 3.26 Gbps ~330
2 128B 12.38 Mpps 6.14 Gbps ~345

1 worker, UDP 64B — limited by the platform
2 workers, UDP 64B — limited by the platform
1 worker, UDP 128B — limited by the platform
2 workers, UDP 128B — limited by the platform

Scaling from one worker to two is near-linear — 6.78 → 13.57 Mpps at 64B is
2.00×, and per-core throughput is unchanged (3.25 → 3.26 Gbps). That is the
useful result in this section: the forwarding path scales with cores rather than
contending.

Per-packet cost is measured, not derived. clocks_per_vector × nodes_per_packet gives ~330 cycles per packet at 64B. This is the most durable
efficiency figure in the report because it survives hardware comparison: on a
2.0 GHz core, 330 cycles is ~165 ns of CPU per packet.

Thread balance is 0.9997–1.0 across the multi-worker cells, confirming the
one-queue-per-worker placement in §3.3 distributes evenly.

The generator ceiling is demonstrated, not merely asserted. A
platform-limited number rises when workers are added. The generator-limited
cells do not move at all — they are byte-identical across every worker count:

Packet size 1 worker 2 3 4 5
512B 4.229 Mpps 4.229 4.229 4.229 4.229
1518B 1.463 Mpps 1.463 1.463 1.463 1.463
128B bng bng 15.2 15.2 15.2
64B bng bng rss 21.93 21.93

Five different dataplane configurations returning one identical figure is the
signature of a constraint that is not the dataplane. The rig's ceilings are
~20 Gbps aggregate, 15.2 Mpps at 128B and 21.93 Mpps at 64B.

For the 512 B and 1518 B rows this is line rate with zero loss: the box
forwarded every packet offered while the link was essentially full. Saturating
it at those frame sizes would need far more than the rig's 20 Gbps, so the cells
are the strongest reading available here, not a shortfall. The 64 B cells at
four and five workers are the genuine floors — there the rig simply under-offered.

Beyond two workers the 64 B cells stop finding a platform ceiling. At three
workers that cell becomes RSS-limited; at four and five the rig under-offers:

3 workers, UDP 64B — limited by RSS
5 workers, UDP 64B — limited by the generators

5.8 Local-AAA performance — run-699-aaa.json

What it measures. Four experiments against the platform's own subscriber
store, each carrying its own limited_by.

Local-AAA experiments

A — authentication latency. limited_by: none. Cost is flat in the
resident base
: 0.14 µs per resident subscriber with r² = 0.10, i.e. no
meaningful slope. Across the measured sweep — bases of 1,000, 3,000, 5,000 and
10,000 — p50 DORA latency sits between 297.8 ms and 301.9 ms. Authenticating
against the largest base measured costs what it costs against the smallest. The
sweep does not extend to the 25,000 licensed cap.

B — single-subscriber provisioning. limited_by: serial-by-design — one
operation in flight on purpose. This is a latency measurement and must not be
read as a provisioning throughput figure.

C — bulk base build. limited_by: controller, so publishable with the
component named. Build time is linear in the resident base (O(N)): 0.05 µs
per resident subscriber, r² = 0.92. Adding one subscriber costs more as the base
grows — relevant when planning bulk imports.

D — local-AAA in-rush. 5,000 of 5,000 bound, completion 100% at
381.3/s, with offered_headroom = 3.07: work arrived 3.1× faster than the
box drained it, so this rate is the platform's own.

The cost of local-AAA admission. Comparing D against the open-admission
cold in-rush in §5.2 — the same storm, differing only in admission — gives
381.3/s against 391.8/s. Local-AAA admission costs 2.7%. This is the only
clean measurement of it in the suite.

5.9 Unauthenticated admission — run-699-unauth.json

What it measures. A layered probe: ICMP, TCP and HTTP from identities at
varying degrees of legitimacy, verifying the walled garden admits what it should
and no more.

Layered unauthenticated-admission verdicts

The baseline identity forwarded correctly at all three layers (ICMP echo-reply,
TCP SYN-ACK, HTTP 200), establishing that the probe itself works before the
restrictive cases are read. All admission checks passed.

5.10 Management surface

The console, licence API, security and authentication suites all passed, with
two exceptions recorded in §6. These cover API key handling, role enforcement,
session security, licence activation and the console's own rendering.


6. Known limitations and non-passing checks

6.1 One failure — the dataplane NIC attribution check

test_vpp_owns_dataplane_nics failed. It is a true positive, and it is the
only failing check in the run.

The console sorts NICs into kernel, DPDK and VPP. A NIC is attributed to
VPP only when it appears in the stored port-assignment list — the bucket
records an assignment an operator made, not what VPP is driving. This box's
ports were never assigned through the console, so its two 40GbE NICs are
correctly shown as DPDK-bound (drv=vfio-pci) but never attributed to VPP,
while VPP forwards on them throughout this run.

The dataplane is unaffected, and this report is the evidence. The same box,
in the same run, onboarded 5,000 subscribers, moved 30 GB, held QoS on 480 of
480 subscribers and forwarded 13.57 Mpps. Onboarding, forwarding, QoS and the
session cap do not consult this list.

What does depend on it is a guard: the same stored list makes the devbind
bind / unbind / reset-to-kernel endpoints refuse an operation on a port VPP is
using. With the list empty that guard is not armed, so such a request is carried
out rather than refused — and it re-renders the dataplane configuration. Nothing
does this unprompted; it requires an operator or a script to ask.

Workaround: assign the dataplane ports on the Dataplane page during
commissioning, before the box carries subscribers. On a box already in service,
do not use those controls to correct the display — use a planned maintenance
window. Tracked as #49.

6.2 Four skipped checks

A skip is a precondition that was absent, not a pass. All four are expected:

Check Why it skipped
test_throughput_recent_nonzero_under_load The aggregate throughput panel showed non-zero correctly, but the per-IP sampler has a known gap. Tracked as console #48.
test_lockout_after_failures_then_admin_unlock The per-IP login rate limit (5 per 300 s) trips before the per-account lockout threshold can be reached from a shared source IP. The two protections are mutually exclusive from one address; the rate limit is the one that fired.
test_a_captive_session_reports_its_lease Known gap, expected on a 1.4.0 controller — a captive session reports no usable lease stamp. Fixed for 1.5, tracked upstream as controller bng#48 — a different tracker from the console #48 above. Not a test limitation.
test_upstream_dscp_remark_bits Not run — the skip reason is stale. The test carries xfail(run=False) with the reason "DSCP remark ACTION ships in the next major release". That stopped being true in 1.3.0: bng_access_input.c calls bng_mark_dscp() on both colours, and the codepoints are configurable per direction (green AF11 / yellow AF12 by default, marking off by default). The shipped feature therefore has no coverage in this run. Raised as a test defect; the marking behaviour itself was not exercised here either way.

6.3 Scale limits worth stating

This is a 5,000-subscriber report and should not be read as a 25,000 one.

  • Session reclamation at high scale. At 18,750 concurrent subscribers under
    sustained churn, the controller's session view stops reclaiming released
    sessions. Nothing is lost — orphans were zero in every run — but the count
    stays at its high-water mark. Reproduced at 10,000; clean at 5,000 and
    below
    , as this run shows (integrity 1.0, zero leaks). Traced upstream to two
    defects, both fixed in controller 1.5. Tracked as bng#50.
  • The 25,000 cap is not validated by this run. The platform has been proven
    to 18,750 concurrent sessions. The 25,000 figure is the licence grant's
    ceiling, not a measured result.
  • Console polling scales with session count. Each console page re-fetches
    the full session list on a timer, so controller load scales with
    (tabs × pages × loops). At high session counts that request can be slow or
    fail; 1.4.0 makes such a failure visible rather than rendering an empty table,
    but retiring the full-list pull is #14. Prefer fewer simultaneously-open
    console tabs during a scale test or maintenance window.
  • No forwarding ceiling is established in either direction by §5.6. The
    policer carried 32.9 GB downstream and 30.7 GB upstream and classified every
    byte, but those are the shaping workload's offered rates, not a search for a
    limit. Forwarding limits are §5.7's subject, and there only 4 of 20 cells
    found one.
  • Lease renewal under churn is not exercised (§5.5).

7. Artifact index

Every figure in this report is a screenshot of the harness's own rendering of
these files — not a chart drawn for the report.

File Size Contents
run-699-t.html 482 KB Full suite report, all 4,668 checks
run-699-t.xml 27 KB JUnit XML
run-699-cpe.json 86 KB DORA, traffic series, QoS per subscriber, policer colours
run-699-coreperf.json 104 KB 24-cell forwarding matrix, per-thread counters
run-699-dora-inrush.json 122 KB Cold and warm in-rush phases, comparison
run-699-churn.json 1,105 KB Churn series, integrity, convergence, scatter
run-699-aaa.json 182 KB Four local-AAA experiments with limited_by
run-699-leasechurn.json 1 KB Cohort sizes, continuity samples
run-699-unauth.json 1 KB Layered admission verdicts

30 figures accompany this report. Each was verified non-blank by byte density
before publication (a blank capture is ~4 bytes per kilopixel; these range from
277 to 400).


8. Appendix — audit trail

Suite totals: 4,668 checks — 4,663 passed, 1 failed, 4 skipped, in 188.1
minutes.

Complete forwarding matrix with every limited_by, so §5.7 can be audited
rather than trusted:

The 20 UDP cells the matrix renders, as the panel classifies each:

Workers 64B 128B 512B 1518B
1 BNG ceiling 6.78 BNG ceiling 6.17 line rate 4.23 line rate 1.46
2 BNG ceiling 13.57 BNG ceiling 12.38 line rate 4.23 line rate 1.46
3 floor (rss) 20.25 line rate 15.20 line rate 4.23 line rate 1.46
4 floor 21.93 line rate 15.20 line rate 4.23 line rate 1.46
5 floor 21.93 line rate 15.20 line rate 4.23 line rate 1.46

Totals: 4 ceiling, 13 line rate zero-loss, 3 floor — matching the panel's own
summary. Four TCP-profile cells exist in the JSON but are not part of the matrix
and are not used here.

Four cells of twenty are maximum-throughput numbers. They are the four quoted in
§5.7, and no other forwarding figure in this report is presented as a platform
capability.

Three-run comparison on identical parameters and hardware, used in §3.1 to
assess core isolation:

Cell Run 696 (no isolcpus) Run 698 (isolcpus) Run 699 (isolcpus)
1 worker, 64B 7.04 Mpps 6.779 6.776
1 worker, 128B 6.328 Mpps 6.156 6.167
2 workers, 64B 13.68 Mpps 13.59 13.57
2 workers, 128B 12.25 Mpps 12.31 12.38

Build provenance: bundle 1.4.0-13, plugin 0.10.36, commit 9a12835,
clean working tree. Pre-flight gate passed all twelve checks before launch.