p4lang / p4c

P4_16 reference compiler
https://p4.org/
Apache License 2.0
665 stars 440 forks source link

If the PSA frame NTK path set to true,the ARP packet will be sent as ipv4 . #4882

Open mayo11 opened 3 weeks ago

mayo11 commented 3 weeks ago

Hello,As a newcomer to the P4 language,I have some question... I found that ebpf backends does not have tc's ingress and egress framework model to offer,only xdp_model. I try to use the PSA framework to make a demo, remove the vlan header in the ingress of tc, and add the vlan header on the egress, so that the NIC port can communicate directly with the vlan interface.

I found that when I set NTK to true, the current packet will pass through return "bpf_redirect(skb->ifindex, BPF_F_INGRESS);" and enter the xdp_helper again, and the arp packet will be xdp_helper modified eth_type to ipv4 type, and it will be directly uploaded when entering the ingress by "return TC_ACT_OK;", and the packet eth_type is still 0x0800, and it is not modified to the original ARP (0x0806),ARP packets cannot be delivered to the port correctly!!

I don't know if it's my P4 code that is problematic, please take a look, thank a lot!!!

the necessary part of generate C code:

    SEC("classifier/tc-ingress")
    int tc_ingress_func(struct __sk_buff *skb) {
        struct psa_global_metadata *compiler_meta__ = (struct psa_global_metadata *) skb->cb;
        if (compiler_meta__->pass_to_kernel == true) {
            bpf_printk("at NTK");
            return TC_ACT_OK;
        }

        if (compiler_meta__->packet_path == NORMAL) {
            compiler_meta__->mark = 153;
            struct internal_metadata *md = (struct internal_metadata *)(unsigned long)skb->data_meta;
            if ((void *) ((struct internal_metadata *) md + 1) <= (void *)(long)skb->data) {
                __u16 *ether_type = (__u16 *) ((void *) (long)skb->data + 12);
                if ((void *) ((__u16 *) ether_type + 1) > (void *) (long) skb->data_end) {
                    return TC_ACT_SHOT;
                }
                *ether_type = md->pkt_ether_type;
                bpf_printk("in ingress ,eth proto %d", *ether_type);
            }
        }

        if (!ostd.drop && ostd.egress_port == 0) {
            compiler_meta__->pass_to_kernel = true;
            bpf_printk("at NTK judge ,eth proto");
            // return TC_ACT_OK;
            return bpf_redirect(skb->ifindex, BPF_F_INGRESS);
        }
        return bpf_redirect(ostd.egress_port, 0);
    }

The P4 code:

    #include <core.p4>
    #include <psa.p4>
    #define ETH_P_8021Q 0x8100

    typedef bit<48> ethernet_addr_t;

    header ethernet_h {
        ethernet_addr_t dstAddr;
        ethernet_addr_t srcAddr;
        bit<16>         etherType;
    }

    header vlan_h {
        bit<3>  pcp;
        bit<1>  dei;
        bit<12> vid;
        bit<16> tpid;
    }

    struct headers_t {
        ethernet_h ethernet;
        vlan_h     vlan;
    }

    struct local_metadata_t {

    }

    struct empty_metadata_t {
    }

    parser packet_parser(packet_in packet, out headers_t hdr, inout local_metadata_t local_metadata, 
                        in psa_ingress_parser_input_metadata_t standard_metadata, in empty_metadata_t resub_meta, 
                        in empty_metadata_t recirc_meta) {
        state start {
            packet.extract(hdr.ethernet);
            transition select(hdr.ethernet.etherType) {
                16w0x8100: vlan;
                default: accept;
            }
        }

        state vlan {
            packet.extract(hdr.vlan);
            transition accept;
        }
    }

    control ingress(inout headers_t hdr, inout local_metadata_t local_metadata, in psa_ingress_input_metadata_t standard_metadata,
                    inout psa_ingress_output_metadata_t ostd) {
        apply {
            if (hdr.vlan.isValid()) {
                hdr.ethernet.etherType = hdr.vlan.tpid;
                hdr.vlan.setInvalid();
            }
            // ostd.egress_port = (PortId_t)0;
            ostd.drop = false;
        }
    }

    control packet_deparser(packet_out packet, out empty_metadata_t clone_i2e_meta, out empty_metadata_t resubmit_meta,
                            out empty_metadata_t normal_meta, inout headers_t hdr, in local_metadata_t local_metadata,
                            in psa_ingress_output_metadata_t istd) {

        apply {
            packet.emit(hdr.ethernet);
            packet.emit(hdr.vlan);
        }

    }

    parser egress_parser(packet_in packet, out headers_t hdr, inout local_metadata_t local_metadata,
                        in psa_egress_parser_input_metadata_t istd, in empty_metadata_t normal_meta, 
                        in empty_metadata_t clone_i2e_meta, in empty_metadata_t clone_e2e_meta) {
        state start {
            packet.extract(hdr.ethernet);
            transition select(hdr.ethernet.etherType) {
                16w0x8100: vlan;
                default: accept;
            }
        }

        state vlan {
            packet.extract(hdr.vlan);
            transition accept;
        }
    }

    control egress(inout headers_t hdr, inout local_metadata_t local_metadata, in psa_egress_input_metadata_t istd, 
                inout psa_egress_output_metadata_t ostd) {
        apply {
            if (!hdr.vlan.isValid()){
                hdr.vlan.setValid();
                hdr.vlan.tpid = hdr.ethernet.etherType;
                hdr.ethernet.etherType = ETH_P_8021Q;
                hdr.vlan.pcp = 0;
                hdr.vlan.dei = 0;
                hdr.vlan.vid = 2;
            }
        }
    }

    control egress_deparser(packet_out packet, out empty_metadata_t clone_e2e_meta, out empty_metadata_t recirculate_meta, 
                            inout headers_t hdr, in local_metadata_t local_metadata, in psa_egress_output_metadata_t istd,
                            in psa_egress_deparser_input_metadata_t edstd) {
        apply {
            packet.emit(hdr.ethernet);
            packet.emit(hdr.vlan);
        }
    }

    IngressPipeline(packet_parser(), ingress(), packet_deparser()) ip;

    EgressPipeline(egress_parser(), egress(), egress_deparser()) ep;

    PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main;
jafingerhut commented 3 weeks ago

I am not sure, but you might get a wider audience for your question by posting it on the general channel of the P4 Zulip instance here https://p4lang.zulipchat.com

Github issues on this repository are better focused on bugs in, or desired enhancements to, the P4 compiler.

If you do ask your question there, I would suggest making it clear which P4 back end you are trying to use, e.g. are you using the p4tc back end? One of the ebpf/ubpf back ends? That wasn't clear to me from your question. I would also recommend publishing your P4 program in a public repository, perhaps on github.com, and linking to it from your question.

fruffy commented 3 weeks ago

@tatry @osinstom

mayo11 commented 2 weeks ago

I am not sure, but you might get a wider audience for your question by posting it on the general channel of the P4 Zulip instance here https://p4lang.zulipchat.com

Github issues on this repository are better focused on bugs in, or desired enhancements to, the P4 compiler.

If you do ask your question there, I would suggest making it clear which P4 back end you are trying to use, e.g. are you using the p4tc back end? One of the ebpf/ubpf back ends? That wasn't clear to me from your question. I would also recommend publishing your P4 program in a public repository, perhaps on github.com, and linking to it from your question.

Thank you for your reply,The back ends I use are ebpf psa, and my P4 example code is the P4 code I posted. Thank you for your suggestion. I will consider raising my questions on P4 Zulip. Thank you