p4lang / switch

Consolidated switch repo (API, SAI and Nettlink)
151 stars 72 forks source link

Parse ethernet header question. #98

Closed dingrui37 closed 6 years ago

dingrui37 commented 6 years ago

I think if the ethernet.etherType <= 0x05DC or 1500(decimal), it is a 802.3 format header. In the parse_ethernet (include/parse.p4 file) function, the select compares the values to find a matching one.

parser parse_ethernet {
    extract(ethernet);
    return select(latest.etherType) {
        0 mask 0xfe00: parse_llc_header;
        0 mask 0xfa00: parse_llc_header;
        ETHERTYPE_BF_FABRIC : parse_fabric_header;
        PARSE_ETHERTYPE;
    }
}

If the length is 512, and the ternart match is not 0, therefore, it cannot be handled by the parse_llc_header function, but it is a 802.3 format. I wonder if it is a problem or a mistake I make.

     0000 0010 0000 0000  (512)
&
     1111 1110 0000 0000  (0xfe00)   -> != 0

     0000 0010 0000 0000  (512)
&
     1111 1010 0000 0000  (0xfa00)   -> != 0
vgurevich commented 6 years ago

@dingrui37 ,

You have a couple of options to achieve what you want, but your program seems to be not quite one of them.

Here are your options:

  1. Use range 0..1500. This range is composed as follows:
    • 0x05dc mask 0xffff / 1500 itself /
    • 0x05d8 mask 0xfffc / 1496..1499 /
    • 0x05d0 mask 0xfff8 / 1488..1595 /
    • 0x05c0 mask 0xfff0 / 1472..1487 /
    • 0x0580 mask 0xffc0 / 1408..1471 /
    • 0x0500 mask 0xff80 / 1280..1407 /
    • 0x0400 mask 0xff00 / 1024..1279 /
    • 0x0000 mask 0xfc00 / 0..1023 /
  2. Use range 64..1500 since ethernet packets cannot be shorter than 64 bytes. I'll leave this as an exercise :)
  3. Use a "simplified range" 0..1535 (0..0x5FF). This is actually what most people do, since it requires only 2 ranges:
    • 0x0400 mask 0xfe00 / 1024..1535 /
    • 0x0000 mask 0xfc00 / 0..1023 /

Happy hacking, Vladimir

dingrui37 commented 6 years ago

@vgurevich, thank you for your reply.

  1. The program is not written by me, it is in the github(https://github.com/p4lang/switch/blob/master/p4src/includes/parser.p4, line 114).
        0 mask 0xfe00: parse_llc_header;
        0 mask 0xfa00: parse_llc_header;
  2. I am sorry for the exercise, because I am still confused. I don't know how to express '>= 64 & <= 1500' using mask.
  3. why the number's mask is 0xffff ? what's the meaning of this mask? 0x05dc mask 0xffff /* 1500 itself */
  4. I think I have understand what's your said in the third point, but I am not sure, because I think the result shoule be 0xfc00 and 0xf800.
    
    -----------------------------------        
    0        0000  0000  0000  0000
               ....
    512      0000  0010  0000  0000
               ....
    1023     0000  0011  1111  1111 
    -----------------------------------
    mask     1111  1100  0000  0000  ==> 0x fc 00

1024 0000 0100 0000 0000 1025 0000 0100 0000 0001 .... 1039 0000 0100 0000 1111 .... 1535 0000 0101 1111 1111

mask 1111 1110 0000 0000 ==> 0x fe 00 1111 1000 0000 0000 ==> 0x f8 00 why not 0xf800 ?

dingrui37 commented 6 years ago

I think perhaps I have some misunderstanding about the mask operator in select expression. I have read the P4_14 spec. In my opinon, the matching process is shown as below. Please point out my mistakes. 1) convert the mask value to binary; 0xfe00 --> 1111 1110 0000 0000; 2) using the 'etherType' value AND 1111 1110 0000 0000, the result's first 7bits will be ignored; For example,

     1500      0000 0101 1101 1100
&   0xfe00     1111 1110 0000 0000
--------------------------------------
               0000 0100 0000 0000    

3) using the result compares with 0, without the first 7bits, the result is match the first entry. 0 mask 0xfe00: parse_llc_header;

parser parse_ethernet {
    extract(ethernet);
    return select(latest.etherType) {
        0 mask 0xfe00: parse_llc_header;
        0 mask 0xfa00: parse_llc_header;
        ETHERTYPE_BF_FABRIC : parse_fabric_header;
        PARSE_ETHERTYPE;
    }
}
vgurevich commented 6 years ago

@dingrui37 ,

The 1's in the mask indicate which bits in the value matter and 0's indicate which bits do not matter.

From the spec:

The mask operator is used to indicate a ternary match should be performed using the indicated mask value. The comparison between the select expression and the case’s value is limited to the bits set in the mask; that is, the select expression and value are each ANDed with the mask before the comparison is made.

dingrui37 commented 6 years ago

@vgurevich I think the correct result is 0xfa00 and 0xfc00, is it right?

    0x0400 mask 0xfa00 /* 1024..1535 */
    0x0000 mask 0xfc00 /* 0..1023 */
vgurevich commented 6 years ago

@dingrui37 ,

Your solution would work, although I prefer the canonical way:

     0x0400 mask 0xfe00 /* 1024..1535 */
     0x0000 mask 0xfc00 /* 0..1023 */

The difference is that in the canonical solution we maintain that value & mask == value. Also, the ranges are non-intersecting (in your solution, the first line also catches values 0..511 as does the second line as well) and masks do not have holes in them. These are often important considerations when it comes down to HW programming.

dingrui37 commented 6 years ago

@vgurevich Thanks.