How to use recirculation on tofino?

Hello Everyone, I would like to ask a question about tofino. I want to know how to use recirculation in tofino. I have found many documents but I cannot found an example of this. Could you write a simple example? Maybe I think that tofino recirculates a packet 4 times and each time will add 1 to the payload, and then send it out. Of course, other simple example is ok. Thank you all!

In TNA, recirculation of packets is accomplished by sending a packet to a port that is in loopback mode. Intel Tofino supports recirculation ports that are never connected to front panel ports and always in loopback mode. The ports that may normally be used to connect to the front panel may also be configured in loopback mode if desired. The P4 code is in control of making the choice for a packet to be sent to a port in loopback mode.

So you either have to configure a port to operate in loopback mode (see Intel forum for details) and then send a packet to that port as usual, or - even simpler - you create a physical loop by plugin a cable into two ports (or directly using a loopback cable).

There is no magic with recirculation on Tofino. Its a “passive” mechanism and not different to sending a packet to any other port (except that the port is configured in a certain way).

I see. I learn that the simpler way is just create a physical loop by plugin a cable into two ports. And the recirculated packet is the same as the normal packet without some special header. I will read the pdf carefully and have a try.
Thank you very much!!

I can recirculate packets now. But I cannot extract pktgen_recirc_header_t through the code below.


parser IngressParser(packet_in        pkt,
    /* User */    
    out my_ingress_headers_t          hdr,
    out my_ingress_metadata_t         meta,
    /* Intrinsic */
    out ingress_intrinsic_metadata_t  ig_intr_md){

    /* This is a mandatory state, required by Tofino Architecture */
    state start {
        pkt.extract(ig_intr_md);
        pkt.advance(16);
        // pkt.advance(PORT_METADATA_SIZE);
        // transition parse_ethernet;
        transition select(ig_intr_md.ingress_port) {
            68 : parse_recirc;
            default: parse_ethernet;
        }
    }
    state parse_recirc {
        pkt.extract(hdr.recirc);
        transition accept;
    }

    state parse_ethernet {
        pkt.extract(hdr.ethernet);
        transition select(hdr.ethernet.ether_type) {
            ETHERTYPE_TPID:  parse_vlan_tag;
            ETHERTYPE_IPV4:  parse_ipv4;
            default: accept;
        }
    }

    state parse_vlan_tag {
        pkt.extract(hdr.vlan_tag);
        transition select(hdr.vlan_tag.ether_type) {
            ETHERTYPE_IPV4:  parse_ipv4;
            default: accept;
        }
    }

    state parse_ipv4 {
        pkt.extract(hdr.ipv4);
        // transition accept;
        transition select(hdr.ipv4.protocol) {
            6: parse_tcp;
            default: accept;
        }
    }
    state parse_tcp {
        pkt.extract(hdr.tcp);
        transition parse_payload;
    }
    state parse_payload {
        pkt.extract(hdr.payload);
        transition accept;
    }
}
control Ingress(
    /* User */
    inout my_ingress_headers_t                       hdr,
    inout my_ingress_metadata_t                      meta,
    /* Intrinsic */
    in    ingress_intrinsic_metadata_t               ig_intr_md,
    in    ingress_intrinsic_metadata_from_parser_t   ig_prsr_md,
    inout ingress_intrinsic_metadata_for_deparser_t  ig_dprsr_md,
    inout ingress_intrinsic_metadata_for_tm_t        ig_tm_md){
>
    action send(PortId_t port) {
        ig_tm_md.ucast_egress_port = port;
        ig_tm_md.bypass_egress = 1;
    }

    action drop() {
        ig_dprsr_md.drop_ctl = 1;
    }

    table ipv4_lpm {
        key     = { hdr.ipv4.dst_addr : exact;}
        actions = { send; drop; }
        
        default_action = send(64);
        size           = IPV4_LPM_SIZE;
    }
    
    apply {
        if (hdr.ipv4.isValid()) {
            hdr.payload.num = hdr.payload.num - 1;
            hdr.payload.value = hdr.payload.value + 1;
            if (hdr.payload.num <= 0){
                ipv4_lpm.apply();
            }
            else{
                send(68);
            }
        }
    }
}

The packet will contain a 6-byte pktgen header immediately after the port metadata header. So I skip 2-byte port metadata header. But now tofino will not send out packet. If I just use “pkt.advance(PORT_METADATA_SIZE);” and don’t extract recirculation header, it can execute normally.
Also, I want to know how to configure packet generators.
Control plane seems to have no operations.
image

If you recirculated a packet by sending it to a port that has an external Ethernet cable that is looped back to another port, then the returning packets will not have a recirculation header on them. They will be plain old ordinary Ethernet frames with nothing different about them as compared to a brand new Ethernet frame coming from a different device.

The recirc header should only be present if you used control software to put a port in loopback mode, and then send a packet to that port.

If you want help on configuring Tofino packet generators, I would suggest contacting official Intel support: Intel(r) Tofino(tm) Family, TNA, and P4Studio questions

Another option is to pay for an appropriate Tofino training course, offered here: https://www.p4ica.com/

Thank you for your reply. Yes, if I recirculate the packet through an external Ethernet cable that is looped back to another port, then the returning packets is the same as the normal packet without special header. But I send the packet to port 68, which is recirculation port mentioned in the pdf.

I will go check out the link, thank you for your suggestion.

If you send a regular packet to a recirculation port, there shouldn’t be a recirculating header on it automatically (only if you manually added one). The pktgen header is only present if the packet was created by the packet generator.

See

If a received packet was created by a packet generator, the packet will contain a 6-
byte pktgen header immediately after the port metadata header. See Section 9 for
more details on packet generation.

Therefore you parse parts of the Ethernet header into your recirc header which (obviously) messes with the remaining control logic.

Regarding the configuration of the traffic generator, you should visit the official intel forum (as suggested by Andy) or you could read through P4TG/Controller/src/core/traffic_gen.rs at main · uni-tue-kn/P4TG · GitHub to get an idea how the control plane endpoints look for the traffic generator (it’s written in rust but the BFRT table names are universal - see Open-Tofino/share/bf_rt_shared/bf_rt_pktgen_tf1.json at master · barefootnetworks/Open-Tofino · GitHub).

OK, I will carefully read the links. I know these questions are very simple, and I truly appreciate your patient responses.
Thank you very much!!