コンテンツにスキップ

17. Architecture description

The architecture description must be provided by the target manufacturer in the form of a library P4 source file that contains at least one declaration for a package; this package must be instantiated by the user to construct a program for a target. For an example see the Very Simple Switch declaration from Section [#sec-vss-arch].

The architecture description file may pre-define data types, constants, helper package implementations, and errors. It must also declare the types of all the programmable blocks that will appear in the final target: parsers and control blocks. The programmable blocks may optionally be grouped together in packages, which can be nested.

Since some of the target components may manipulate user-defined types, which are unknown at the target declaration time, these are described using type variables, which must be used parametrically in the program—i.e., type variables are checked similar to Java generics, not C++ templates.

\~ Figure { #fig-switcharch; caption: “Fragment of example switch architecture.” } [switcharch] \~ [switcharch]: figs/switcharch.png { width: 75%; page-align: here }

The following example describes a switch by using two packages, each containing a parser, a match-action pipeline, and a deparser:

\~ Begin P4Example parser Parser(packet_in b, out IH parsedHeaders); // ingress match-action pipeline control IPipe\<T, IH, OH>(in IH inputHeaders, in InControl inCtrl, out OH outputHeaders, out T toEgress, out OutControl outCtrl); // egress match-action pipeline control EPipe\<T, IH, OH>(in IH inputHeaders, in InControl inCtrl, in T fromIngress, out OH outputHeaders, out OutControl outCtrl); control Deparser(in OH outputHeaders, packet_out b); package Ingress\<T, IH, OH>(Parser p, IPipe\<T, IH, OH> map, Deparser d); package Egress\<T, IH, OH>(Parser p, EPipe\<T, IH, OH> map, Deparser d); package Switch(Ingress\<T, , > ingress, Egress\<T, , > egress); \~ End P4Example

Just from these declarations, even without reading a precise description of the target, the programmer can infer some useful information about the architecture of the described switch, as shown in Figure [#fig-switcharch]:

  • The switch contains two separate packages Ingress and Egress.
  • The Parser, IPipe, and Deparser in the Ingress package are chained together in order. In addition, the Ingress.IPipe block has an input of type Ingress.IH, which is an output of the Ingress.Parser.
  • Similarly, the Parser, EPipe, and Deparser are chained in the Egress package.
  • The Ingress.IPipe is connected to the Egress.EPipe, because the first outputs a value of type T, which is an input to the second. Note that the occurrences of the type variable T are instantiated with the same type in Switch. In contrast, the Ingress type IH and the Egress type IH may be different. To force them to be the same, we could instead declare IH and OH at the switch level: package Switch<T,IH,OH>(Ingress<T, IH, OH> ingress, Egress<T, IH, OH> egress).

Hence, this architecture models a target switch that contains two separate channels between the ingress and egress pipeline:

  • A channel that can pass data directly via its argument of type T. On a software target with shared memory between ingress and egress this could be implemented by passing directly a pointer; on an architecture without shared memory presumably the compiler will need to automatically synthesize serialization code.
  • A channel that can pass data indirectly using a parser and deparser that serializes data into a packet and back.

To construct a program for the architecture, the P4 program must instantiate a top-level package by passing values for all its arguments creating a variable called main in the top-level namespace. The types of the arguments must match the types of the parameters—after a suitable substitution of the type variables. The type substitution can be expressed directly, using type specialization, or can be inferred by a compiler, using a unification algorithm like Hindley-Milner.

  • For example, given the following type declarations:
    Begin P4Example parser Prs(packet_in b, out T result); control Pipe(in T data); package Switch(Prs p, Pipe map);

    End P4Example

  • and the following declarations:
    Begin P4Example parser P(packet_in b, out bit\<32> index) { /* body omitted / } control Pipe1(in bit\<32> data) { / body omitted / } control Pipe2(in bit\<8> data) { / body omitted */ }

    End P4Example

  • The following is a legal declaration for the top-level target:
    Begin P4Example Switch(P(), Pipe1()) main;

    End P4Example

  • And the following is illegal:
    Begin P4Example Switch(P(), Pipe2()) main;

    End P4Example

The latter declaration is incorrect because the parser P requires T to be bit<32>, while Pipe2 requires T to be bit<8>.

The user can also explicitly specify values for the type variables (otherwise the compiler has to infer values for these type variables):

\~ Begin P4Example Switch\<bit\<32>>(P(), Pipe1()) main; \~ End P4Example

\~ Figure { #fig-packetfilter; caption: “A packet filter target model. The parser computes a Boolean value, which is used to decide whether the packet is dropped.” } [packetfilter] \~ [packetfilter]: figs/packetfilter.png { width: 5cm; page-align: here }

To illustrate the versatility of the P4 architecture description language, we give an example of another architecture: one which models a packet filter that makes a drop/no drop decision based only on the computation in a P4 parser, as shown in Figure [#fig-packetfilter].

This model could be used to program packet filters running in the Linux kernel. For example, we could replace the tcpdump language with the much more powerful P4 language; P4 can seamlessly support new protocols, while providing complete “type safety” during packet processing. For such a target, the P4 compiler could generate an eBPF (Extended Berkeley Packet Filter) program, which is injected by the tcpdump utility into the Linux kernel, and executed by the eBPF kernel JIT compiler/runtime.

In this case the target is the Linux kernel, and the architecture model is a packet filter.

  • The declaration for this architecture is as follows:
    Begin P4Example parser Parser(packet_in packet, out H headers); control Filter(inout H headers, out bool accept);
  • package Program(Parser p, Filter f);
    End P4Example