p4lang / behavioral-model

The reference P4 software switch
Apache License 2.0
544 stars 329 forks source link

Question: how to use multicast and clone function on the bmv2? #667

Closed dingrui37 closed 6 years ago

dingrui37 commented 6 years ago

Hi @antoninbas , I want to try multicast and clone function on the bmv2, but the description about multicast and clone in the p4runtime spec is based on the PSA arch, while the simple_switch target is on the top of v1model, so I want to confirm whether bmv2 supports these functions and how to use.

When I want to use multicasting, the mcast_grp field in the standard_metadata must be set. The value is equal to the session_id field in CloneSessionEntrymessage from control plane. The type of session_id is uint32, while the type of mcast_grp is bit<16>, does this means the session_id must be bit<16> for bmv2, even though its type is uint32?

What is the priority between the three fields, egress_spec, clone_spec and mcast_grp? In other words, when I set egress_spec, clone_specand mcast_grpon the same time, which one will take effect?

struct standard_metadata_t {
 ...
 bit<9>  egress_spec;
 bit<32> clone_spec;
 ...
 @alias("intrinsic_metadata.mcast_grp")     bit<16> mcast_grp;
 ...
}
message Replica {
  uint32 egress_port = 1;
  uint32 instance = 2; 
}

message MulticastGroupEntry {
  uint32 multicast_group_id = 1;
  repeated Replica replicas = 2;
}

message CloneSessionEntry {
  uint32 session_id = 1;
  repeated Replica replicas = 2;
  uint32 class_of_service = 3;
  int32 packet_length_bytes = 4;
}
jafingerhut commented 6 years ago

In your question, I think you may be mixing up mcast_grp and clone session id values. mcast_grp should be set to a multicast group id value, which numerically might happen to be equal to one of the valid clone session id values, but mcast_grp never refers to or uses a clone session id.

See this documentation to see if it answers your questions for P4_16+v1model, at least, which as it mentions early in the doc, is not identical to the behavior defined in PSA, but should be what the open source bmv2 simple_switch code does: https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md

antoninbas commented 6 years ago

@jafingerhut is correct. Cloning to a multicast group is very similar to cloning to a unicast port. You need to call the clone primitive, which will set standard_metadata.clone_spec. The value you pass to the clone primitive is the clone session id. At runtime, you need to map that clone session id to either a unicast port number or a multicast group id, using P4Runtime or the thrift CLI (P4Runtime actually lets you do both!). When you clone to a multicast group, you do not set mcast_grp in your P4 program. If you set mcast_grp in addition to calling clone, the original packet will be multicast in addition to the clone packet, so you will end up with 2xN packets in egress, where N is the size of your multicast group. You can also multicast the original packet to one group and multicast the cloned packet to a different multicast group. So mcast_grp and clone_spec are not mutually exclusive.

dingrui37 commented 6 years ago

Thanks @jafingerhut @antoninbas this document is very helpful for me. I have another question.

_You need to call the clone primitive, which will set standard_metadata.clonespec. The value you pass to the clone primitive is the clone session id.

Is this description only just for P4_14? In P4_16 version, I can set the clone_spec field directly.

standard_metadata.clone_spec = 1;

antoninbas commented 6 years ago

Yes you need to call the clone or clone3 primitive for P4_16 v1model programs, you should not be accessing the clone_spec field directly. bmv2 simple_switch uses the field to encode more than just the session id.

dingrui37 commented 6 years ago

I write a little code to test multicast, I want to multicast arp request packet to all port(only two ports), my code is as below:

control MyIngress(inout my_headers_t hdr,
                  inout my_metadata_t meta,
                  inout standard_metadata_t standard_metadata) {

    action drop() {
        mark_to_drop();
        exit;
    }

    //use data-plane bound parameter only for test
    action multicast(in bit<32> multicast_group_id) {
        clone(CloneType.I2E, multicast_group_id);
    }

    table forward {
        key = {
            hdr.arp.isValid()      : exact;
            hdr.arp.oper           : ternary;
            hdr.arp_ipv4.isValid() : exact;
        }

        actions = {
            drop;
            multicast(1);
        }

        const default_action = drop();
        const entries = {
            (true, ARP_OPER_REQUEST, true) : multicast(1);
        }
    }

    apply {
        forward.apply();
    }
}

Multicast group entry from the controller:

P4Runtime Write
updates {
  type: INSERT
  entity {
    packet_replication_engine_entry {
      multicast_group_entry {
        multicast_group_id: 1
        replicas {
          egress_port: 1
          instance: 1
        }
        replicas {
          egress_port: 2
          instance: 2
        }
      }
    }
  }
}

The question is I don't find any packet clone to egress as expected, I capture packet at port 2 but find nothing. Log:

[19:49:52.478] [bmv2] [D] [thread 19405] mgrp node created for mgid 1
[19:49:52.478] [bmv2] [D] [thread 19405] node created for rid 2
[19:49:52.478] [bmv2] [D] [thread 19405] node associated with mgid 1
[19:49:52.487] [bmv2] [D] [thread 19405] node created for rid 1
[19:49:52.487] [bmv2] [D] [thread 19405] node associated with mgid 1
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Processing packet received on port 1
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser 'parser': start
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser 'parser' entering state 'start'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Extracting header 'ethernet'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser state 'start': key is 0806
[19:51:28.988] [bmv2] [T] [thread 19438] [17.0] [cxt 0] Bytes parsed: 14
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser 'parser' entering state 'parse_arp'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Extracting header 'arp'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser state 'parse_arp': key is 000108000604
[19:51:28.988] [bmv2] [T] [thread 19438] [17.0] [cxt 0] Bytes parsed: 22
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser 'parser' entering state 'parse_arp_ipv4'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Extracting header 'arp_ipv4'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser state 'parse_arp_ipv4' has no switch, going to default next state
[19:51:28.988] [bmv2] [T] [thread 19438] [17.0] [cxt 0] Bytes parsed: 42
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Parser 'parser': end
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Pipeline 'ingress': start
[19:51:28.988] [bmv2] [T] [thread 19438] [17.0] [cxt 0] Applying table 'MyIngress.forward'
[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Looking up key:
* hdr.arp.$valid$     : 01
* hdr.arp.oper        : 0001
* hdr.arp_ipv4.$valid$: 01

[19:51:28.988] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Table 'MyIngress.forward': hit with handle 0
[19:51:28.989] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Dumping entry 0
Match key:
* hdr.arp.$valid$     : EXACT     01
* hdr.arp.oper        : TERNARY   0001 &&& ffff
* hdr.arp_ipv4.$valid$: EXACT     01
Priority: 1
Action entry: MyIngress.multicast -

[19:51:28.989] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Action entry is MyIngress.multicast -
[19:51:28.989] [bmv2] [T] [thread 19438] [17.0] [cxt 0] Action MyIngress.multicast
[19:51:28.989] [bmv2] [T] [thread 19438] [17.0] [cxt 0] Primitive (no source info)
[19:51:28.989] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Pipeline 'ingress': end
[19:51:28.989] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Cloning packet at ingress
[19:51:28.989] [bmv2] [D] [thread 19438] [17.0] [cxt 0] Egress port is 0
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Pipeline 'egress': start
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Pipeline 'egress': end
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Deparser 'deparser': start
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Deparsing header 'ethernet'
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Deparsing header 'arp'
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Deparsing header 'arp_ipv4'
[19:51:28.989] [bmv2] [D] [thread 19439] [17.0] [cxt 0] Deparser 'deparser': end
[19:51:28.989] [bmv2] [D] [thread 19443] [17.0] [cxt 0] Transmitting packet of size 42 out of port 0
dingrui37 commented 6 years ago

Maybe I misunderstand your advice @antoninbas, I change my code as below, it works. That is to say, when you need to clone a packet, you need to use cloneor clone3, while for multicast, you can set the mcast_grp directly?

    action multicast(in bit<32> multicast_group_id) {
        standard_metadata.mcast_grp = 1;
    }
[21:01:00.309] [bmv2] [T] [thread 21562] [20.0] [cxt 0] Action MyIngress.multicast
[21:01:00.309] [bmv2] [T] [thread 21562] [20.0] [cxt 0] packet-replication.p4(155) Primitive standard_metadata.mcast_grp = 1
[21:01:00.309] [bmv2] [D] [thread 21562] [20.0] [cxt 0] Pipeline 'ingress': end
[21:01:00.309] [bmv2] [D] [thread 21562] [20.0] [cxt 0] Multicast requested for packet
[21:01:00.309] [bmv2] [D] [thread 21562] number of packets replicated : 2
[21:01:00.309] [bmv2] [D] [thread 21562] [20.0] [cxt 0] Replicating packet on port 2
[21:01:00.309] [bmv2] [D] [thread 21562] [20.0] [cxt 0] Replicating packet on port 1
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Pipeline 'egress': start
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Pipeline 'egress': start
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Pipeline 'egress': end
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Deparser 'deparser': start
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Deparsing header 'ethernet'
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Deparsing header 'arp'
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Deparsing header 'arp_ipv4'
[21:01:00.309] [bmv2] [D] [thread 21564] [20.2] [cxt 0] Deparser 'deparser': end
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Pipeline 'egress': end
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Deparser 'deparser': start
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Deparsing header 'ethernet'
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Deparsing header 'arp'
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Deparsing header 'arp_ipv4'
[21:01:00.309] [bmv2] [D] [thread 21565] [20.1] [cxt 0] Deparser 'deparser': end
[21:01:00.309] [bmv2] [D] [thread 21567] [20.2] [cxt 0] Transmitting packet of size 42 out of port 1
[21:01:00.309] [bmv2] [D] [thread 21567] [20.1] [cxt 0] Transmitting packet of size 42 out of port 2
antoninbas commented 6 years ago

I may not have been very clear. Here are the different things you can do: 1) multicast packet. You need to set standard_metadata.mcast_grp to a multicast group id and configure that multicast group id with a P4Runtime MulticastGroupEntry message. 2) clone packet to a set of ports. You need to call clone or clone3 and provide a clone session id. You need to configure that session id with a P4Runtime CloneSessionEntry message. The packet will be cloned to every port you include in the replicas Protobuf field. It can be a single port or several ports. Behind the scenes, bmv2 will implement the P4Runtime message by creating a multicast group, so you will see mentions of multicast groups in the logs, but this is just how bmv2 implements cloning to multiple ports.

For a given packet, you can do both 1 & 2. As mentioned by both Andy and I, you do not call clone with a multicast group id, you always provide a clone session id that is configured at runtime.

There are examples for both 1 & 2 here: https://github.com/p4lang/behavioral-model/blob/master/targets/simple_switch_grpc/tests/test_pre.cpp

dingrui37 commented 6 years ago

Does bmv2 not support clone currently? The write RPC give an exception.

P4Runtime Write
updates {
  type: INSERT
  entity {
    packet_replication_engine_entry {
      clone_session_entry {
        session_id: 2
        replicas {
          egress_port: 1
          instance: 1
        }
        replicas {
          egress_port: 2
          instance: 2
        }
        class_of_service: 2
        packet_length_bytes: 64
      }
    }
  }
}
[09:01:23.860] [bmv2] [E] [thread 1367] [P4Runtime] The only PRE operations currently supported are for multicast
antoninbas commented 6 years ago

You need to update your PI code

jafingerhut commented 4 years ago

Your question was: "After cloning a packet in ingress stage, does the cloned version of the packet again goes to parser before being processed in egress stage?"

The answer for the BMv2 v1model architecture implementation using the simple_switch or simple_switch_grpc command is: yes, it does. This detail was added to the "after-ingress pseudocode" in this file on 2020-Feb-02, after I realized that it does happen:

https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md

In particular, this paragraph:

Each cloned packet will be processed by your parser code again. In many cases this will result in exactly the same headers being parsed as when the packet was most recently parsed, but if your parser code uses the value of standard_metadata.instance_type to affect its behavior, it could be different.

Regards, Andy

On Sat, Mar 21, 2020 at 10:23 AM Debobroto Das Robin < notifications@github.com> wrote:

I have a slightly different question, but it is relevant that's why asking it here.

Question: After cloning a packet in ingress stage, does the cloned version of the packet again goes to parser before being processed in egress stage?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/p4lang/behavioral-model/issues/667#issuecomment-602075401, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA2YPPD7G73NETMLFE4SADRITZYPANCNFSM4FZTAV2A .

drobinkent commented 4 years ago

Are multicasted packets also goes through the parser again, just like cloned packets?

jafingerhut commented 4 years ago

No. Packets cloned from ingress are intended to be as nearly like they were when they arrived at the beginning of ingress processing, with no modifications made to them during ingress processing.

Multicast packets are more like unicast packets, in that any and all changes made to them during ingress processing should take effect on them, before they are sent onwards to egress processing.