Skip to content

Type Translation

Type translation is a P4Runtime mechanism that decouples controller-facing values from data-plane values. P4 programs declare translated types with the @p4runtime_translation annotation — but the P4Runtime spec leaves the actual mapping mechanism unspecified. Every deployment rolls its own. 4ward ships a built-in translation engine that handles this automatically.

The problem

A P4 program might declare:

@p4runtime_translation("", string)
type bit<9> port_id_t;

The controller sends "Ethernet0" (a string). The P4 program processes a 9-bit integer. Something has to map between these — but the P4Runtime spec doesn't say how. Both sdn_string (like SAI P4's port names) and sdn_bitwidth (fixed-width integers in a wider SDN representation) are supported.

Translation modes

4ward supports three modes:

  • Auto-allocate (default) — 4ward assigns data-plane values on first use. Zero config. The controller sends any value; 4ward maps it to 0, 1, 2, ...
  • Explicit — you provide the full mapping table upfront. Unknown values are rejected.
  • Hybrid — pin the values that matter, auto-allocate the rest.
Hybrid mode example — pin special ports, auto-allocate the rest:

  explicit:  "CpuPort"    → 510
  explicit:  "DropPort"   → 511
  auto:      "Ethernet0"  →   0  (assigned on first use)
  auto:      "Ethernet1"  →   1
  auto:      "Ethernet2"  →   2

Configuring mappings

There are two ways to provide explicit mappings.

In P4 source: @p4runtime_translation_mappings

The P4 program itself can declare mappings inline:

@p4runtime_translation("", string)
@p4runtime_translation_mappings({
  {"CpuPort", 510},
  {"DropPort", 511}
})
type bit<9> port_id_t;

The 4ward p4c backend extracts these at compile time and converts them to translation entries with auto-allocate enabled (hybrid mode). This is the most convenient approach — the mappings live with the type declaration.

At pipeline load: DeviceConfig.translations

Alternatively, configure mappings in SetForwardingPipelineConfig:

# proto-file: @fourward//simulator/ir.proto
# proto-message: fourward.DeviceConfig
translations {
  type_name: "port_id_t"
  auto_allocate: true
  entries { sdn_str: "Ethernet0"  dataplane_value: "\x00" }
  entries { sdn_str: "Ethernet1"  dataplane_value: "\x01" }
}

This is useful when the controller, not the P4 program, owns the mapping.

Without either source of configuration, auto-allocation is used by default.

Where translation happens

Operation Direction What is translated
Write (table entry, action profile) P4RT → dataplane Match fields, action params
Read (table entry, action profile) Dataplane → P4RT Match fields, action params
PacketOut P4RT → dataplane Metadata fields
PacketIn Dataplane → P4RT Metadata fields
InjectPacket Either direction Ingress port (p4rt_ingress_port oneof)

Translation only applies to fields whose p4info type_name resolves to a @p4runtime_translation-annotated type.

Ports are special

Most translated types appear in table entries — match fields and action parameters. Ports are different: they appear in hardcoded proto fields across multiple messages (InjectPacketRequest.ingress_port, OutputPacket.egress_port, PacketIn/PacketOut metadata, clone session replicas).

4ward creates a port translator when the architecture's port metadata field uses a type (not typedef) with @p4runtime_translation. Stock architectures use typedef — no port translation. Programs that need port translation use a forked architecture.

Traces

When translation is available, trace trees carry P4RT values alongside dataplane values — port names on ingress/egress events, and translated match fields and action params on table lookups. See P4RT enrichment.