StacKAT: Infinite-State Network Verification

PLDI 2025

Jules Jacobs (Cornell)
Nate Foster (Cornell)
Tobias Kappé (Leiden)
Dexter Kozen (Cornell)
Lily Saada (Cornell)
Alexandra Silva (Cornell)
Jana Wagemaker (Radboud)

Packet Processing

SWITCH
-
SRC
-
DST
-
PORT
-
VLAN
-
TTL
-

NetKAT: Network Verification

Input Packet:
SWITCH
S1
SRC
10.0.0.1
DST
10.0.0.101
PORT
80
VLAN
100
TTL
64
Processing Program:
while(ttl > 0) {
  if (dst == 10.0.0.101) {
    vlan = 200
    ttl = ttl - 1
    switch = S4
  }
  ...
}
Output Packet:
SWITCH
S4
SRC
10.0.0.1
DST
10.0.0.101
PORT
80
VLAN
200
TTL
63

Key Insight

Network behavior = Program execution

  • Packet field = local variable
  • Match rule = if statement
  • Action = variable update
  • Multi-hop = loops
  • Verification = program equivalence

NetKAT: Kleene algebra with tests for network verification

NetKAT Limitations

Fixed number of fields with fixed number of bits

  • ❌ No variable-length data
  • ❌ Doesn't model packet parsing      
  • ❌ Cannot handle tunneling
  • ❌ Cannot handle SR / MPLS
  • ❌ Cannot model unbounded state
  • ❌ Limited to finite-state verification
We need to model infinite state.

StacKAT: Infinite State Modeling

Packet with Variable-Length Payload:
SWITCH
S1
SRC
10.0.0.1
DST
10.0.0.101
PORT
80
VLAN
100
TTL
64
PAYLOAD
0111010110010110101001...

StacKAT extends NetKAT with a stack to model variable-length payload data

Known from NetKAT: verification = program equivalence
Main challenge: decidable infinite state equivalence

StacKAT: Packet Processing with Payload

SWITCH
-
SRC
-
DST
-
PORT
-
VLAN
-
TTL
-
PAYLOAD
-

The StacKAT Language

e ::= e₁ + e₂ | e₁ · e₂ | e* | f := n | f == n |
push(n) | pop(n)

NetKAT Operations

0 Drop packet (fail)
1 Forward packet (skip)
e₁ + e₂ Non-deterministic choice
e₁ · e₂ Sequential composition
e* Kleene star (iteration)
f := n Set field f to constant n
f == n Test field f equals n

StacKAT Extensions

push(n) Push constant n onto stack
pop(n) Pop from stack, test if equals n

Key insight: Stack operations enable modeling of variable-length data and unbounded state

Stack Operations

push(3)

Before
[1, 2]
.
After
[3, 1, 2]

pop(3)

Before
[3, 1, 2]
.
After
[1, 2]
✓ Test passed

pop(5) - Test fails

Before
[3, 1, 2]
.
After
✗ Top was 3, not 5 → packet dropped

pop(3) - Empty stack

Before
[ ]
.
After
✗ Stack was empty → packet dropped

Common Programming Patterns

if f==0 then p else q

f==0·p + f==1·q

while f==0 do p

(f==0·p)*·f==1

push(f)

f=0·push(0) +
f=1·push(1) +

f := pop()

pop(0)·f:=0 +
pop(1)·f:=1 +

StacKAT Semantics

A StacKAT program defines a relation between input and output packets

Input Packet
Header Fields
src
dst
port
Payload (Stack)
[v₁, ..., vₙ]
[e]
Output Packet
Header Fields
src'
dst'
port'
Payload (Stack)
[w₁, ..., wₘ]

Each input packet is mapped to zero or more output packets

0 outputs
packet dropped
1 output
deterministic
2+ outputs
non-deterministic

Compositional Semantics

Sequencing: ⟦e₁ · e₂⟧ = ⟦e₁⟧ ∘ ⟦e₂⟧ Relation composition
Choice: ⟦e₁ + e₂⟧ = ⟦e₁⟧ ∪ ⟦e₂⟧ Relation union
Kleene star: ⟦e*⟧ = ⟦e⟧* Reflexive transitive closure
Field test: ⟦f == n⟧ Filter packets where field f equals n
Field update: ⟦f := n⟧ Set field f to value n
Push: ⟦push(n)⟧ Push constant n onto stack
Pop: ⟦pop(n)⟧ Pop and test equals n

The Key Verification Question

Is ⟦e₁⟧ = ⟦e₂⟧ ?

Do two programs have the same semantics?

NetKAT

  • Finite number of possible packets
  • Finite-state automata
  • ✓ Decidable (PSPACE-complete)

StacKAT

  • Unbounded stack size
  • Infinite number of possible packets
  • Decidable or undecidable?
Surprising: It's decidable! (EXPSPACE-complete)

Basic StacKAT Equivalences

    push(v)·pop(v) ≡ 1 // push then pop cancels out
    push(v)·pop(w) ≡ 0 // mismatched values drop packet (v ≠ w)
pop(v)·push(v) + 1 ≡ 1 // pop-push has no effect when it succeeds
These rules capture the basic interactions between push and pop operations

A Surprising Equivalence

push(a)* · pop(a)*  ≡  push(a)* + pop(a)*

Why? After push-pop cancellation, the net effect is either:
• Only pushes remain (if more pushes than pops)
• Only pops remain (if more pops than pushes)
• Nothing remains (if equal pushes and pops)

Which Programs are Equivalent?

pop(v)* · push(v)*
(push(v) + pop(v))*
push(v)* · (pop(v) · pop(v))*
(pop(v) · pop(v))* · push(v)*
pop(v)* · (push(v) · push(v))*
(push(v)* · pop(v))*
push(v)* + pop(v)*
(push(v) · pop(v))*

Interactive Demo

Try entering StacKAT programs to see their automata and test equivalence

Verification Algorithm

Goal: Check language equivalence L(A₁) = L(A₂) after normalization

StacKAT Program

push(3)* · pop(3)*
Convert
Initial automaton
Step 1
Push-pop closure
Add ε-transitions
After push-pop closure
Step 2
Pop-push filtering
Intersect with pop*push*
After pop-push filtering
Step 3
Zipping
Normalized automaton
Normalized automaton

Step 0: Creating the Automaton

Idea: use small-step rules to generate finite automata

State is ⟨header fields, expression⟩  →  finite state!

Stack handled via labeled transitions

Step 1: Push-Pop Closure

Push-ε-Pop rule

Rule 1: Add epsilon shortcut when push(v) followed by pop(v)

ε-ε chain rule
Self-loop rule

Rule 2: Maintain transitive reflexive closure of ε edges

Step 2: Pop-Push Filtering

Intersect automaton with pop*push* language:

After push-pop closure
Removes inconsistent push(v) pop(w) transitions
Cancels out matching push(v) pop(v) transitions

Step 3: Zipping the Automaton

Run the automaton "middle-out",
synchronizing push and pop actions

Initial
After pop-push filtering
After pop-push filtering
Step 1
Zip automaton
Pairs represent synchronized inside-out actions
After combining
Step 2
Add initial-stack self-loops
Final normalized automaton
Final zipped automaton
Theorem. L(A₁) = L(A₂) ⟺ ⟦e₁⟧ = ⟦e₂⟧

Aside: Visibly Pushdown Languages

Is this the same as visibly pushdown languages? No!

VPLs

  • ✓ Restricted stack behavior
  • ✓ Separate input tape

StacKAT

  • ✓ Unrestricted stack behavior
  • ✓ Input = initial stack (packet)
Key Insight: Adding an input tape to StacKAT would make equivalence undecidable

Time Complexity

Pure Stack Fragment
PSPACE
complete
With Header Fields
EXPSPACE
complete

Performance

Challenge: Scaling to large header space enumeration
Future Direction: Development of symbolic algorithm

Complete Axiomatization

Definition: An axiomatization is complete if every valid equation can be derived from the axioms

Ideal Axioms

  •  push(v)⋅pop(v) ≡ 1
  •  push(v)⋅pop(w) ≡ 0 (v ≠ w)
  •  pop(v)⋅push(v) ≤ 1

Current Axioms

  •  push(v)⋅pop(v) ≡ 1
  •  push(v)⋅pop(w) ≡ 0 (v ≠ w)
  •   (ugly third axiom hidden)

Future work: are the ideal axioms complete?

Conclusion

StacKAT is a new model for unbounded packet processing with decidable equivalence

Future Work

Surface Syntax
Symbolic Algorithm
Ideal Axiomatization

Goal: verification of real world large networks