NNSR Internal Compact Header Roadmap
Status: Planned
Last reviewed: 2026-06-22
Purpose
The target SD-WAN internal forwarding format is a 4-byte MPLS-style compact label named NNSR-C. It is an internal header: the label is carried inside the payload protected by the hop-by-hop WireGuard or QUIC underlay. A transit agent reads it only after local underlay authentication and decryption, then re-encrypts the packet to the next hop.
This document intentionally does not specify a public clear-label SD-WAN core. NexusNet trusts WireGuard/BoringTun and QUIC/TLS 1.3/REALITY for cryptographic protection instead of adding a custom end-to-end AEAD layer.
Diagram Name
The ASCII box drawings below are RFC-style packet layout diagrams, also called RFC-style bit diagrams or packet header bit-field diagrams. They show field boundaries by bit position and octet position.
Design Basis
The closest wire-format reference is MPLS label switching:
- RFC 3032 defines a 4-octet label stack entry with a 20-bit label, 3-bit traffic class field, 1-bit bottom-of-stack flag, and 8-bit TTL.
- The forwarding operation is label lookup, optional label replacement or pop, TTL update, and next-hop forwarding.
- That behavior matches SD-WAN transit after underlay decapsulation: transit nodes should not re-run policy matching or own path computation.
Other RFC-shaped options are less suitable for the default NNSR header:
| Candidate | Header Cost | Fit |
|---|---|---|
| MPLS-style label shim, RFC 3032 model | 4 bytes per label | Best compact label-swap shape. |
| Geneve, RFC 8926 | 8 bytes plus options | Flexible, but larger and more general than needed. |
| IPv6 SRH / SRv6, RFC 8754 | 8-byte base plus 16 bytes per segment | Too large for compact public SD-WAN paths. |
MTU constraints push the design toward a small internal shim:
- IPv6 Internet links must support at least a 1280-octet MTU.
- UDP applications should avoid sending datagrams larger than the path MTU.
- QUIC endpoints must handle at least 1200-byte UDP datagrams, so 1200 bytes is a useful conservative planning baseline for QUIC-backed paths.
4-Byte NNSR-C Header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Label / SID | TC |S| TTL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Fields:
| Field | Bits | Meaning |
|---|---|---|
Label / SID | 20 | Node-local forwarding label in the active route generation. |
TC | 3 | Traffic class. Initially zero; reserved for QoS, OAM, fallback class, or drop class. |
S | 1 | Bottom-of-stack flag. Set to 1 for the initial single-label format. |
TTL | 8 | Hop limit. Transit decrements it and drops at zero. |
Encoding rules:
- The 4-byte compact label is encoded in network byte order.
Label / SIDoccupies bits 31 through 12 of the 32-bit word.TCoccupies bits 11 through 9.Soccupies bit 8.TTLoccupies bits 7 through 0.
Octet encoding:
| Octet | Bits | Encoding |
|---|---|---|
| 0 | 7..0 | Label[19:12] |
| 1 | 7..0 | Label[11:4] |
| 2 | 7..4 | Label[3:0] |
| 2 | 3..1 | TC[2:0] |
| 2 | 0 | S |
| 3 | 7..0 | TTL |
The initial format uses one label only. Label stacking remains reserved for future service chaining or nested overlays, but the default SD-WAN path should stay single-label.
WG NNSR Packet Layout
WG NNSR means NNSR-C is the first bytes of the decrypted WireGuard tunnel payload. On the public wire, the compact label is encrypted and authenticated by the hop-by-hop WireGuard session.
Public underlay packet:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ Outer IPv4 Header or Outer IPv6 Header ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UDP Source Port | UDP Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UDP Length | UDP Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| WireGuard Message Type = Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| WireGuard Receiver Index |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ WireGuard Counter +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ AEAD ciphertext of: NNSR-C Label + Inner IPv4/IPv6 ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ WireGuard AEAD Tag (16 octets) ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Payload visible to the agent after WireGuard decapsulation:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Label / SID | TC |S| TTL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| |
+---+ +
| |
~ Inner IPv4 or IPv6 Packet ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+WG NNSR decrypted payload offsets:
| Offset | Length | Field |
|---|---|---|
| 0 | 4 | NNSR-C compact label. |
| 4 | variable | Inner IPv4 or IPv6 packet. The first nibble is 4 or 6. |
WG NNSR overhead before the inner IP packet:
| Layer | Bytes |
|---|---|
| Outer IPv4 header | 20 |
| Outer IPv6 header | 40 |
| UDP header | 8 |
| WireGuard data header | 16 |
| WireGuard AEAD tag | 16 |
| NNSR-C label | 4 |
This gives 64 bytes before inner IP on IPv4 underlay and 84 bytes before inner IP on IPv6 underlay. Link-layer overhead is outside this count.
QUIC Tunnel NNSR Packet Layout
QUIC Tunnel NNSR means NNSR-C is inside a QUIC DATAGRAM after QUIC packet protection has authenticated and decrypted the packet. The compact label is not visible to an external observer on the public wire.
Public underlay packet:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ Outer IPv4 Header or Outer IPv6 Header ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UDP Source Port | UDP Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UDP Length | UDP Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ QUIC Header (long or short, variable) ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ QUIC packet-protected payload, including DATAGRAM frame ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+QUIC DATAGRAM frame after QUIC packet protection is removed:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DATAGRAM Frame Type (QUIC varint, usually 1B) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Optional DATAGRAM Length (QUIC varint, if present) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
~ DATAGRAM Application Data ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Preferred Nexus QUIC Tunnel NNSR application data after QUIC decapsulation:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Label / SID | TC |S| TTL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| |
+---+ +
| |
~ Inner IPv4 or IPv6 Packet ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Preferred QUIC Tunnel NNSR application-data offsets:
| Offset | Length | Field |
|---|---|---|
| 0 | 4 | NNSR-C compact label. |
| 4 | variable | Inner IPv4 or IPv6 packet. The first nibble is 4 or 6. |
HTTP/3 DATAGRAM compatible application data can keep an existing context prefix:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Quarter Stream ID / Context ID (QUIC varint, 4B) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Label / SID | TC |S| TTL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| |
+---+ +
| |
~ Inner IPv4 or IPv6 Packet ~
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+To keep NNSR-C 32-bit aligned in this compatible mode, SD-WAN QUIC contexts should use the QUIC varint range that encodes in 4 bytes:
16384 <= context_id <= 1073741823HTTP/3-compatible offsets:
| Offset | Length | Field |
|---|---|---|
| 0 | 4 | Quarter Stream ID / Context ID, encoded as a 4-byte QUIC varint. |
| 4 | 4 | NNSR-C compact label. |
| 8 | variable | Inner IPv4 or IPv6 packet. The first nibble is 4 or 6. |
Forwarding Semantics
Transit lookup key:
(route_generation, local_label)Transit lookup value:
action: swap | pop | drop
next_label
next_node_short_id
underlay_profile_id
mtu
counter ids
egress_action_id, only for pop entriesTransit behavior:
decrypt/authenticate local WG or QUIC underlay
parse 4-byte NNSR-C label
if TTL <= 1: drop and count ttl_expired
lookup (generation, label)
if miss: drop and count missing_forward_entry
if action = swap:
replace label with next_label
decrement TTL
send via next hop WG or QUIC underlay
if action = pop:
strip 4-byte header
execute egress actionIngress behavior:
match packet against SD-WAN policy
select active path and first local label from runtime table
prepend NNSR-C label with configured TTL
send via first hop WG or QUIC underlayEgress behavior:
decrypt/authenticate local WG or QUIC underlay
pop label
execute NAT, remote_tun, local_route, service_upstream, or dropWhy Remove 18 Bytes
The existing 18-byte header carries fields that are redundant on the fast path:
| Current Field | Compact Replacement |
|---|---|
path_id | Label table entry metadata. |
hop_index | Encoded by local label progression. |
path_len | Not needed by transit; TTL prevents loops. |
ip_version | Inner packet first nibble after pop. |
flow_hash | Underlay session, UDP source port, QUIC connection routing, or table metadata. |
payload_len | Datagram length from QUIC DATAGRAM, WG packet, or packet buffer length. |
The 14-byte saving is not the whole MTU story, but it compounds with QUIC, WireGuard, NAT, and conservative MSS planning. Keeping both formats would also split runtime negotiation, tests, UI naming, and operational diagnosis, so the old codec should be deleted instead of retained as debug mode.
Public Internet Encapsulation
The public wire format is never:
Outer IP / UDP
NNSR-C clear label
Inner IPv4/IPv6 packetThe production public wire format is:
Outer IP / UDP
WG or QUIC protected packet
encrypted/authenticated payload:
NNSR-C compact label
Inner IPv4/IPv6 packetThis means:
- Public observers cannot read NNSR labels.
- A transit node can read NNSR only after decrypting its local underlay session.
- The next hop can decrypt because the previous hop re-encrypts the NNSR payload into the next hop's WG or QUIC session.
- Encryption cost is hop-by-hop and scales with the number of underlay links in the path.
eBPF And Userspace Fit
NNSR-C is small enough for fast userspace processing. It is not a commitment to public pre-decrypt tc/XDP forwarding.
Allowed optimization paths:
- Userspace parses, decrements TTL, swaps the 20-bit label, and forwards through the selected WG/QUIC adapter.
- QUIC and WG workers can batch decrypt, lookup, and enqueue next-hop sends.
- A future post-decap helper can optimize label lookup after underlay protection is removed.
- A clear-label tc/eBPF mode may exist only as a non-default controlled profile.
Not a near-term goal:
- NIC-level eBPF reading NNSR-C directly from public packets.
- Public clear-label label swap as the normal SD-WAN mode.
Implementation Plan
- Add a versioned NNSR-C codec.
- Extend backend runtime generation with per-hop local labels and action type:
swap,pop, ordrop. - Add userspace compact transit tests for ingress, swap, pop, TTL expiry, missing label, and route generation replacement.
- Add runtime feature negotiation for
nnsr_compact_label_v1. - Wire NNSR-C through QUIC REALITY and BoringTun WG adapters.
- Migrate frontend path inspector to display both logical path and compact label entries.
- Switch all new route generations to compact labels.
- Add regression tests that new runtime config emits only NNSR-C over protected underlay sessions.
Open Decisions
| Decision | Current Direction |
|---|---|
| Label scope | Node-local per route generation. |
| Underlay security | Hop-by-hop WG or QUIC/TLS 1.3; no custom NNSR AEAD. |
| Stack support | Reserve S; single-label default. |
| TC semantics | Initially zero; reserve for OAM/fallback/QoS classes. |
| Authentication | Underlay authentication plus generation-scoped labels. |
| Replay handling | Underlay-specific; compact label does not solve replay by itself. |
| Fragmentation | Avoid fragmentation through MTU/MSS planning and PLPMTUD-style probing. |
References
- RFC 3032: MPLS Label Stack Encoding.
- RFC 2784: Generic Routing Encapsulation.
- RFC 7348: Virtual eXtensible Local Area Network.
- RFC 8754: IPv6 Segment Routing Header.
- RFC 8926: Geneve.
- RFC 8085: UDP Usage Guidelines.
- RFC 8200: IPv6 Specification.
- RFC 9000: QUIC.