P4 control flow

Hello all,
I have this code in p4:

if(standard_metadata.ingress_port == CPU_PORT) {
  if(!mac_table.apply().hit) {
     standard_metadata.egress_spec = (bit<9> ) hdr.pktOut.egress_port;
  }
  else {
        standard_metadata.ingress_port = (bit<9> ) hdr.pktOut.egress_port;
  }
  hdr.pktOut.setInvalid();
}           
else if((hdr.ethernet.etherType == TYPE_ARP) || (hdr.ethernet.etherType == TYPE_IPV4)){
    smac_table.apply();
    mac_table.apply();
}

When i try to run this i get this error:

Program is not supported by this target, because
table MyIngress.mac_table has multiple successors
table mac_table {
^^^^^^^^^
The mac_table.apply is in different if statements,can p4 recognize these difference or should i change my p4 code?

The BMv2 software switch has several limitations. One more obvious one to tell from a program is “it cannot apply the same table more than once per packet”, but the actual restriction is more subtle than that. The compiler must be able to determine for each table a unique “next table” that can be applied (if I recall correctly the precise restriction – it is tricky to explain).

Typically, if you can find a way to restructure your code so that a table.apply() occurs exactly once in your code, that should compile fine. It is usually possible to do this, although it might not be a tiny change in your code to achieve this.

@andyfingerhut can you modify the ingress_port metadata? I am not sure if this kind of metadata is read only. Else, I guess user metadata works too to store the port (see next answer).

Without knowing the purpose of the program, plenty of times, egress_port for a packet out is straightforward, so this following code might make sense (you can also re-structure your code to meet the requirements Andy mentioned):

if(standard_metadata.ingress_port == CPU_PORT) {
  standard_metadata.egress_spec = (bit<9> ) hdr.pktOut.egress_port;
  hdr.pktOut.setInvalid();
           
} else if((hdr.ethernet.etherType == TYPE_ARP) || (hdr.ethernet.etherType == TYPE_IPV4)){
    smac_table.apply();
    mac_table.apply();
}

Cheers,

@ederollora The open source p4c compiler for the v1model architecture lets you assign values to standard_metadata.ingress_port without giving an error. This is because the standard_metadata struct in v1model is a direction inout parameter to the ingress control [0], so the language spec allows assignments to be made to all or any part of such an inout parameter.

One could imagine changing the v1model architecture definition to put things that should be read-only into a separate struct that is direction in to the ingress control, as TNA does (see below), but that would require a change to the parameter list of the ingress control, affecting all programs written for the v1model architecture.

One could also imagine adding special checks for such things in the v1model back end of p4c, which has been suggested [4], but the implementation is not very straightforward, and such checks would be custom to the v1model architecture, not enforced by the language itself.

Whether an actual physical switch ASIC would ever let you do this is up to the switch ASIC vendor. I am not aware of a switch ASIC that implements exactly the v1model architecture as defined in open source p4c and BMv2. If there is a switch ASIC that does, I do not know whether it allows such assignments.

As a point of comparison in a different P4 architecture, TNA (Tofino Native Architecture), the ingress_port field is defined in a struct named ingress_intrinsic_metadata_t [1], and in the ingress control this struct is a direction in parameter [2]. All fields that can be assigned values in the ingress control are put into other structs that are separate parameters to the ingress control with direction out or inout.

Thus for programs written for TNA, it is a compile-time error to attempt to assign ingress_port a value in the ingress control, because the P4_16 language spec says this it is an error to modify any value that is a direction in parameter (see Section 6.7 “Calling convention: call by copy in/copy out” in the language spec [3]).

[0] p4c/v1model.p4 at main · p4lang/p4c · GitHub
[1] Open-Tofino/tofino.p4 at master · barefootnetworks/Open-Tofino · GitHub
[2] Open-Tofino/tofino1arch.p4 at master · barefootnetworks/Open-Tofino · GitHub
[3] P4~16~ Language Specification
[4] standard_metadata.egress_port should not be assigned in egress or ingress pipeline · Issue #1917 · p4lang/p4c · GitHub

1 Like