Recompute IPv4 Payload Size in SRH: "We currently support only single stage actions."

I’m trying to extract an IPv4 packet from an SRH6 packet and hitting a problem in the action:

    bit<16> srv6h_size = (((bit<16>)hdr.srh.hdrExtLen + 1) << 4) + 8;
    hdr.ipv4.total_len = hdr.ipv6.payload_len - srv6h_size;

Explanation. hdr.srh.hdrExtLen is the number of octets in SRH header less 1.
hdr.ipv6.payload_len is the size of the payload. In my case, I have a payload length of 124, an ExtLen of 4, and therefore the ipv4.total_len should be 84 (124 - (4+1)*8) = 84.

I get the message:

/home/p4/p4srv6_new/include/srv6.p4(338): [–Werror=unsupported] error: shl: action spanning multiple stages. Operations on operand 2 ($tmp6[0..15]) in action srv6_end_m_gtp4_e require multiple stages for a single action. We currently support only single stage actions. Please rewrite the action to be a single stage action.
bit<16> srv6h_size = (((bit<16>)hdr.srh.hdrExtLen + 1) << 4) + 8;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/p4/p4srv6_new/include/srv6.p4(329)
action end_m_gtp4_e() {
^^^^^^^^^^^^
/home/p4/p4srv6_new/include/srv6.p4(339): [–Werror=unsupported] error: add: action spanning multiple stages. Operations on operand 2 ($tmp7[0..15]) in action srv6_end_m_gtp4_e require multiple stages for a single action. We currently support only single stage actions. Please rewrite the action to be a single stage action.
hdr.ipv4.total_len = hdr.ipv6.payload_len - srv6h_size;
^^^^^^^^^^
/home/p4/p4srv6_new/include/srv6.p4(329)
action end_m_gtp4_e() {
^^^^^^^^^^^^

Number of ERRORs: 2
Number of WARNINGs: 15

Number of errors exceeded set maximum of 1

Where am I going wrong please?

Thanks

David

Is this using the P4 compiler for Tofino? If not, what P4 compiler are you using?

If it is for Tofino, basically there is a small finite number of dependent arithmetic operations you can do in one stage.

You are asking it to start with hdr.srh.hdrExtLen, add 1 to it, then shift that left by 4, then add 8 to that, then subtract the result from hdr.ipv6.payload_len. That is a sequence of 4 dependent arithmetic operaitons.

I do not know the precise limits, but the compiler does, and gives an error like the one you show when you exceed it.

One option you can explore is to move some of the computations to an earlier action, e.g. calculate srv6h_size in an earlier action, storing the result in a metadata field, then use that metadata field in the current action.

Thank you - it is for Tofino and I’m using 9.10.0.

I did wonder if it was an arithmetic limit which I presume is to ensure that the forwarding is predicatble (which makes a lot of sense).

I’ve moved the size calculation for the SIDs out of the action section and have it working exactly as you suggested.

Thank you for the very quick reply.

David

The latest versions of the Tofino P4 compiler (including the one available as a part of open-p4studio) can automatically split complex actions into multiple stages.

However, more often than not you will pay the price, so it usually makes sense to review your algorithm to make it more Tofino-friendly.

In your case, the best bet would be to create a table that will match on hdr.srh.hdrExtLen and compute the whole thing in one stage.

Here is an example prototype:

action compute_ipv4_total_len(bit<16> srv6h_size) {
    hdr.ipv4.total_len = hdr.ipv6.payload_len - srv6h_size;
}

table ipv4_total_len {
    key = { hdr.srh.hdrExtLen : exact; } 
    actions = { compute_ipv4_total_len; }
    size = 256; 
    const entries  = {
        0: compute_ipv4_total_len(24),  // (0 + 1) << 4 + 8
        1: compute_ipv4_total_len(40),  // (1 + 1) << 4 + 8
        2: compute_ipv4_total_len(56),
        . . . 
      255: compute_ipv4_total_len(4104) // (255 + 1) << 4 + 8
   }
}
. . . 
apply {
     // Compute the new value of hdr.ipv4_total_len
     ipv4_total_len.apply();
}

This is not something a compiler can currently do, but this is (I think) the most efficient way to do it on Tofino.

Happy hacking,
Vladimir

Thank you!

I’ve doen something quite similar to that by noting the number of SIDs associated with an SRH in the main SRv6 action and then modifying the size of the IPv6 overall payload later in the egress parsing.

I am just downloading the new P4 Studio but 9.10.0 has met my needs for several years now :-1:

As always, thank you for the help!

David