It’s been a year since the P4 language was first proposed in an ACM CCR article. In the past year, the language has evolved from a proposal into a specification. This blog post captures some of the thinking behind this evolution: what features were added to P4 and why? This post is based on a recent paper, co-authored with Changhoon Kim, Ramkumar Krishnamurthy, Advait Dixit, and Mihai Budiu, that appeared at the ACM Symposium On SDN Research 2015.
P4, as originally proposed, had a few core concepts, summarized below:
- The abstract switch model: From P4’s perspective, what does the switch look like? The abstract switch model specifies that the switch has a P4-programmable parser, followed by a P4-programmable ingress pipeline, followed by a fixed traffic manager, followed by a P4-programmable egress pipeline.
- Headers: The fields in a header and their lengths. For instance: Ethernet headers have a 48b source MAC address field, a 48b destination MAC address field, and a 16b EtherType field.
- Parsers: How are headers sequenced in a packet, and how do they identify encapsulated inner headers? A canonical example is parsing an Ethernet header, whose EtherType identifies IPv4 as the next header type, whose protocol ID tells us that the next header is TCP.
- Tables and Actions: How are packets processed by the switch? P4 builds upon OpenFlow’s match-action table abstraction, but allows programming both the match and action components. The match key is any combination of header fields, including (possibly not yet defined) user-defined header fields. The actions are built from a set of action primitives that operate on packet fields (arithmetic, read/write, compare, add/remove fields).
- The control flow graph: Which match-action tables process a packet and in what order? For instance, the graph could specify that a packet should be processed by an Access Control List table before it is processed by an IP routing table.
Figure 1: The P4 abstract switch model
Our open-source P4 development environment consists of a compiler and software switch to compile and run P4 programs. Within this development environment, we used P4 to express the forwarding behavior of a datacenter switch, with a feature set (listed in the table below) comparable to current shared-memory switches: including VLANs, ACLs, Tunnels, and ECMP. The goal of this exercise was to empirically evaluate P4’s expressiveness using a common example of packet processing: the forwarding behavior of a datacenter switch. The P4 program used in the paper, DC.p4, is available in the P4 github repository, and we use it to illustrate specific aspects of P4.
Figure 2: The P4 development environment
This exercise taught us several lessons. On a positive note, the premise of building larger actions from simple action primitives was true for over 90% of the actions in DC.p4. Second, we observed that the match-action table abstraction was an intuitive abstraction for most network engineers — primarily because switches have really always structured packet processing as set of table lookups.
In the process, we also found that the original P4 proposal was unable to express some kinds of packet processing. One of these was Equal-Cost Multi-Pathing (ECMP), a load-balancing mechanism used in datacenters to spread out traffic among a set of candidate outgoing links. Without ECMP, routing could work as follows: think of the match key as the destination address, and the action just selects the output port based on this key. However, to implement ECMP, there isn’t a 1:1 mapping from destination address to output port. Instead, the ECMP output port is selected dynamically for a given flow based on a hash of its 5-tuple. Moving from a 1:1 match-action mapping to a “one-to-one-of-many” action mapping required us to introduce the concept of an action profile. An action profile allows a match key to select one action from a set based on an action selector. In ECMP’s case, this selector is a hash of the 5-tuple.
We also added a few new action primitives; we summarize the most interesting of these here. Packet cloning allows us to copy a packet and send it either to another port (for applications such as mirroring), or to the switch CPU. Digest generation extracts a set of fields from a packet, packages them into a digest, and sends them to a receiver. MAC learning is a well-known use case of digest generation. These language additions: action profiles, new action primitives, and several more detailed in the paper are now part of the current P4 specification.
By and large, we find that P4 hits the right level of abstraction for packet processing. Concretely, there is no bit twiddling required to manipulate header fields. Instead, the fields in a header are accessed through defined header types, and the compiler automatically generates the packet parser using the P4 parser specification.
At the same time though, there is room for improvement. First, P4’s support for modularity is limited. Currently, we break out distinct pieces of P4 code into separate P4 files using include directives. These are then stitched back together by the C preprocessor before being parsed by P4. Include directives are a rather weak form of modularity: every included P4 file can read/write all headers read/written by any other included P4 file. Second, some P4 semantics are imprecise. For instance, what happens when an integer packet field overflows: does it wraparound or saturate? Lastly, although adding new action primitives is expected, it risks bloating the language with a large set of opaque keywords that the compiler cannot reason about — and hence cannot optimize. It may also be a sign that we are adding primitives to P4 singularly based on our experience with one specific target architecture; some of these primitives may not be available or even implementable on other targets.
To counter this tendency towards adding new action primitives, we first note that P4 could also be used for programming the data plane of network devices other than switches, such as a network-interface cards, firewalls, or access points. These network devices, which we call packet-processing engines (PPEs), have significant diversity in their underlying packet-processing architectures. This diversity, in turn, implies that no single abstract switch model is likely expressive enough for all these PPEs.
We propose a simple solution to resolve this: move the abstract switch model out of the language and into a separate specification all by itself, which we call the PPE architecture. The PPE architecture specifies the interconnect between the programmable and fixed-function blocks (for instance, the fact that a programmable ingress pipeline connects to a fixed scheduler, which then connects to a programmable egress pipeline).
A vendor supplying a PPE architecture for her device might also include a vendor library that details the input-output interface for all functions provided exclusively by that vendor. Action primitives such as packet cloning and digest generation, which had to be added to the P4 language specification, could now be part of vendor libraries, which are in turn part of a specific vendor PPE. If they turn out to be widely useful, they could eventually migrate into a standard P4 library. Overall, moving some P4 constructs like action primitives into library components would simplify the language core. For a more detailed look at this proposal, we refer the reader to a slidedeck on the topic, presented by Mihai Budiu at the recent P4 workshop.
We hope this post has given you a flavor for the evolution of P4 from a proposal into a concrete specification over the last year. The paper has many more technical details than we can hope to cover in one post. We think this form of empirical analysis of P4 programs is useful in two ways. On the one hand, it provides a more rigorous basis for language design. On the other, it allows us to catalogue the action primitives that are most frequently used, providing switch designers with useful feedback for designing programmable hardware. We encourage you to compile, run, and extend DC.p4 and to participate in the ongoing discussion by writing P4 programs for other packet-processing applications.
About the author: Anirudh Sivaraman is a graduate student at MIT’s Computer Science and Artificial Intelligence Laboratory. He is broadly interested in computer networking and his recent research work is in the area of programmable forwarding planes.