p4lang / behavioral-model

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

Support header stacks references in expressions #152

Closed antoninbas closed 7 years ago

antoninbas commented 8 years ago
cw299 commented 8 years ago

Is the support of keyword last still under development? simple_switch throws an error message if my p4 program tries to access the largest-index valid instance of a header stack (inside of an action):

terminate called after throwing an instance of 'std::out_of_range' what(): _Map_base::at

My code looks like:

action my_action() {
    modify_field(stack[last].field, 1);
}
antoninbas commented 8 years ago

This is still under development. It is likely that I will wait until the new P4 (P4-16) spec comes out so that I can make sure the bmv2 implementation is consistent with the spec.

cw299 commented 8 years ago

I try to access an element of a stack via an index variable. The index variable should be represented by a metadata field. But a metadata field is not an integer data type. Accordingly, the following example won't work:

modify_field(my_metadata.idx, 0);
modify_field(stack[my_metadata.idx].field, 1);

Is there any way to cast an metadata field to an integer?

antoninbas commented 8 years ago

It would be useful to see what kind of error you are getting. However, AFAIK:

cw299 commented 8 years ago

The compiler gives the following error:

user@desktop:~/behavioral-model/targets/simple_switch$ p4c-bmv2 simple_switch.p4 --json simple_switch.json parse error in file /[...]/simple_switch.p4 at line 151 : Syntax error while parsing at token my_metadata (ID) parse error in file /[...]/simple_switch.p4 at line 151 : Invalid index in array header reference 2 errors during parsing Interrupting compilation Error while building HLIR

Background: I try to process a sequence of MPLS labels that are attached to an incomming packet. The number of MPLS labels is unknown until the Parser counts them and stores the result into a metadata field. The labels themselves are serialized to a separate stack. Each label of the determines an output port. The task of the ingress/egress pipeline is to replicate the packet (maybe by clone* operations) to these ports. However, loops are not allowed in P4, but I need to iterate the stack somehow.

antoninbas commented 8 years ago

There is probably a workaround that does not involve using a metadata field as an index into the header stack. For example, if you have a reasonable upper bound to the stack depth, you can always hand-write different actions like this:

table mpls_count {
    reads {
        my_metadata.count : exact;
    }
    actions { count_is_1; count_is_2; count_is_3; count_is_4; ...}
}

Obviously this is very verbose, but can be reasonable for up to say 8 mpls labels. When doing replication, you may want to use the Packet Replication Engine. clone only lets you do one copy per control-flow. But you can always use clone in a "cascading fashion": clone the original packet, then clone the clone, clone the clone of the clone... This works very well if you are allowed to remove (aka pop) the MPLS label after replicating to the port. Actually if you are allowed to do that, this whole thing becomes much easier because all you ever need to look at is stack[0].

cw299 commented 8 years ago

Thanks for your suggestions, great workarounds! The second workaround (cascading cloning) seems to be more flexible. I tried to implement this, but it posed another problem ;-) The Parser seems not to reset it's index counter for the MPLS stack. Please take a look at the following log:

[14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Processing packet received on port 0 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser 'parser': start [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser state 'start' has no switch, going to default next state [14:43:21.168] [bmv2] [T] [thread 9330] Bytes parsed: 0 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Extracting header 'ethernet' [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser state 'parse_ethernet' has no switch, going to default next state [14:43:21.168] [bmv2] [T] [thread 9330] Bytes parsed: 14 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Extracting to header stack 0, next header is 1 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser set: setting field (4, 7) from expression, new value is 1 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser state 'parse_stack': key is 04 [14:43:21.168] [bmv2] [T] [thread 9330] Bytes parsed: 34 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Extracting to header stack 0, next header is 2 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser set: setting field (4, 7) from expression, new value is 2 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser state 'parse_stack': key is 00 [14:43:21.168] [bmv2] [T] [thread 9330] Bytes parsed: 54 [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Parser 'parser': end [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Pipeline 'ingress': start [14:43:21.168] [bmv2] [T] [thread 9330] [0.0] [cxt 0] Applying table 'table1' [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Looking up key:

[14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Table 'table1': miss [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Action entry is action1 - [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Pipeline 'ingress': end [14:43:21.168] [bmv2] [D] [thread 9330] [0.0] [cxt 0] Cloning packet at ingress

All right, the clone is created.

[14:43:21.168] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser 'parser': start [14:43:21.168] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser state 'start' has no switch, going to default next state [14:43:21.168] [bmv2] [T] [thread 9330] Bytes parsed: 0 [14:43:21.168] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Extracting header 'ethernet' [14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser state 'parse_ethernet' has no switch, going to default next state [14:43:21.169] [bmv2] [T] [thread 9330] Bytes parsed: 14 [14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Extracting to header stack 0, next header is 1 [14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser set: setting field (4, 7) from expression, new value is 3

My expected value is 1!

[14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser state 'parse_stack': key is 04 [14:43:21.169] [bmv2] [T] [thread 9330] Bytes parsed: 34 [14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Extracting to header stack 0, next header is 2 [14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser set: setting field (4, 7) from expression, new value is 4

My expected value is 2!

[14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser state 'parse_stack': key is 00 [14:43:21.169] [bmv2] [T] [thread 9330] Bytes parsed: 54 [14:43:21.169] [bmv2] [D] [thread 9330] [0.1] [cxt 0] Parser 'parser': end

[...]

[14:43:21.169] [bmv2] [D] [thread 9330] [0.4] [cxt 0] Extracting header 'ethernet' [14:43:21.169] [bmv2] [D] [thread 9330] [0.4] [cxt 0] Parser state 'parse_ethernet' has no switch, going to default next state [14:43:21.169] [bmv2] [T] [thread 9330] Bytes parsed: 14 [14:43:21.169] [bmv2] [D] [thread 9330] [0.4] [cxt 0] Extracting to header stack 0, next header is 1 terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc

This error might occur because the stack index is out of bounds.

antoninbas commented 8 years ago

Assuming you are using a metadata field as your index counter, the easiest explanation is that you are including this field (or its parent header) in the cloning field list, which is the second parameter of the clone primitive. When a packet is cloned, all the metadata fields are set to 0 in the copy, except for the fields included in the cloning field list, which are preserved. All you would have to do is remove the metadata field used as an index from the cloning field list. If this does not explain your issue, you may have to share with me some P4 code so I can reproduce it.

cw299 commented 8 years ago

Thanks, this was exactly my problem. I removed the counter from the cloning field list, now it's working fine.

You can find attached the resulting p4 program. Here is how it works: An incoming packet contains a stack of IPv4 addresses. The p4 program processes the stack and generates a separate packet (by clone operations) for each IPv4 address.

stack_processing.zip

antoninbas commented 7 years ago

Support for s[last] in expressions was added by #292. s[next] is IMO only needed in parser when extracting.