How to parse packet-out packet from p4runtime?

I want to inject a packet from the control plane to the data plane. I followed the instruction of p4runtime-shell/usage/Packet IO and added @controller_header("packet_out") to my p4 program. But I don’t know how to define headers struct and do the parser part.

  1. Should the headers be like
@controller_header("packet_out") {
    bit<8>  egress_port;
}
struct headers {
    packetout_t  packetout;
    /* standard headers */
    ethernet_t  ethernet;
    ipv4_t  ipv4;
}
  1. How to parse the packets? Specifically, how parser distinguishes packet-out packets from data packets and implement it in code?

Here is one sample P4 program that can be run using simple_switch_grpc that can send packets to the controller, and can parser packets sent to it by the controller. The “controller” in this case is just a Python program that uses the PTF Python library to run some simple automated tests: p4-guide/packetinout.p4 at master · jafingerhut/p4-guide · GitHub

See the README file in that directory for instructions on how to run it (it uses additional Python library files outside of that directory, so you must clone the entire repo in order for running the commands mentioned to work): p4-guide/ptf-tests/packetinout at master · jafingerhut/p4-guide · GitHub

As you can see at the beginning of the parser code in the P4 program, it starts by distinguishing packets arriving on the CPU port from those arriving on other ports, and parses packets arriving on the CPU port differently than it parses packets arriving on normal ports: p4-guide/packetinout.p4 at master · jafingerhut/p4-guide · GitHub

Thanks, I copied the parser part from your code and it worked. Here I have some new questions.

I want to forward packets from CPU port to other hosts. I add the following code to my ingress processing (header packetout only contains a 8-bit egress_port field).

apply {
    if (hdr.packetout.isValid()) {
        standard_metadata.egress_spec = hdr.packetout.egress_port;
    } else {
        // Processing of data packets
    }
}

then I try to dump the packets on the egress interface with tcpdump to see if the packets are being forwarded correctly. However I only captured data packets, no packet-out packets were recorded. So what a packet-out packet looks like?Does it just contain metadata and payload?Can it be dumped with tools like tcpdump?Is there something wrong with my P4 program?

One of the best ways I know of debugging P4 program behavior is to enable logging when you start the simple_switch process, and then look at those logs to see what happens to your packets as they are being processed. It can be tedious to do, but once you find out exactly how your P4 code is behaving on the packets of interest to you, it tells you everything that happened on most, if not all, of the P4 lines of code executed on those packets.

It can be tedious, and it helps find the packets of interest to you if you can reproduce the problem with as few packets as possible, to make it easier to find the one of interest to you in the log, ideally only one packet sent to the switch if you can arrange that.

A packet-out packet when it arrives to the switch starts with the controller header that you define, followed immediately by whatever packet that the controller sent. There should not be anything else in it. Note that the controller header is just a sequence of bytes, just like the packet that follows it, so the only way you can distinguish these from other packets is knowing that they arrived to the switch on the CPU port. You typically will want to call setInvalid() on the controller header to remove it from your packet before it leaves the switch, since if you sent the packet to another device expecting Ethernet frames with that controller header still in front of it, that device would interpret those bytes as the beginning of the Ethernet header.

Does packet-out packet have Ethernet field and IP field? What is left in the header if I call setInvalid() to remove controller header?

So a golden rule to remember is that packets are sequences of bits. How those bits are interpreted is determined by the receiving system that parses those bits.

What arrives to the CPU port of the simple_switch (or simple_switch_grpc) process is a sequence of bits containing first the contents of the controller_header packetout metadata fields, formatted according to how you defined that header in your P4 program. After that comes a sequence of bits as sent by your controller as the packet contents. Whether that begins with an Ethernet header followed by an IP header, or an Ethernet header followed by an ARP header, is entirely up to your P4Runtime controller software. It need not start with an Ethernet header. It is up to software that you write to determine what it contains.