p4lang / p4c

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

Calculating TCP and UDP checksum in p4 switch #2472

Closed sarashakeri closed 4 years ago

sarashakeri commented 4 years ago

Hi,

I am using a P4 switch for implementing nat between two network namespaces and as I am changing the IP addresses I want to calculate l3 and l4 checksum. There is no problem with calculating l3, however, I did not find any example of calculating l4 checksum. Currently, I am using the v1model, but don't have any limitation for the p4 switch model. Is there any way to implement tcp or udp checksum in P4 switch?

hesingh commented 4 years ago

First, the UDP checksum is defined in this RFC: https://tools.ietf.org/html/rfc768 UDP uses the same checksum at TCP.

Pseudo code for the checksum algorithm can be leveraged from the PSA specification.

https://p4.org/p4-spec/docs/PSA.html#appendix-internetchecksum-implementation

sarashakeri commented 4 years ago

Thanks for your reply. What I was looking for, is the already tested example of calculating TCP checksum especially in the V1model ( like what there is for IP header), but it seems there is not a complete and available code for that, right? So, I have to write the code?

hesingh commented 4 years ago

I poked around (not hard enough) the p4lang/behavioral-model repo for simple_switch and do not see a checksum computation. Also, since you are looking for a checksum computation, this issue does not belong to p4c, a compiler. You should file an issue with p4lang/behavioral-model,

What the PSA specification has for checksum computation is correct and the code can be used.

mihaibudiu commented 4 years ago

In general you don't compute the checksum, you incrementally update it, which should be much easier.

hesingh commented 4 years ago

The update_checksum is a simple API you can apply for updating layer-4 checksum.

https://github.com/p4lang/p4c/blob/master/p4include/v1model.p4#L465

There are several example uses in p4c/testdata/p4_16_sample/*.p4 for update_checksum. Even though, the example use IPv4 header, you can replace such data with layer-4 data.

sarashakeri commented 4 years ago

I tried to add something like there is for IPv4 checksum update for TCP header as below, however, it is not working. Any idea about the reason?

` //Calculating tcp length

state parse_ipv4 {
    packet.extract(hd.ipv4);
    verify(hd.ipv4.version == 4w4, error.IPv4IncorrectVersion);
    verify(hd.ipv4.ihl == 4w5, error.IPv4OptionsNotSupported);
    meta.tcpLength = hd.ipv4.total_len - 20;
    transition accept;
}

//Updating checksum

control my_compute_checksum(inout headers_t hdr,
                          inout metadata_t meta)
{
    apply {
         update_checksum(
            hdr.ipv4.isValid(),
            { hdr.ipv4.version,
              hdr.ipv4.ihl,
              hdr.ipv4.diffserv,
              hdr.ipv4.total_len,
              hdr.ipv4.identification,
              hdr.ipv4.flags,
              hdr.ipv4.frag_offset,
              hdr.ipv4.ttl,
              hdr.ipv4.protocol,
              hdr.ipv4.src_addr,
              hdr.ipv4.dst_addr },
            hdr.ipv4.hdr_checksum,
            HashAlgorithm.csum16);
       update_checksum(
                hdr.tcp.isValid(),
                { hdr.ipv4.src_addr,
                  hdr.ipv4.dst_addr,
                  8w0,
                  meta.tcpLength,
                  hdr.ipv4.protocol,
                  hdr.tcp.src,
                  hdr.tcp.dst,
                  hdr.tcp.seq,
                  hdr.tcp.ack,
                  hdr.tcp.offset,
                  hdr.tcp.resrv,
                  hdr.tcp.ecn,
                  hdr.tcp.ctrl,
                  hdr.tcp.window,
                  hdr.tcp.urgent,
                },
                hdr.tcp.checksum,
                HashAlgorithm.csum16);

    }

}`

I also found the PSA code for incremental checksum update but I have no idea how to write it for v1model.

jafingerhut commented 4 years ago

The code you have shown will calculate a checksum based upon the explicitly listed fields you show in the arguments to the call, but does not include the TCP payload data, which by the TCP RFC should be included for a correct TCP checksum.

It might be that replacing your second update_checksum call with update_checksum_with_payload instead will calculate a correct TCP checksum. I have not verified that myself.

sarashakeri commented 4 years ago

@jafingerhut, Thanks for your reply. I changed it to "update_checksum_with_payload", it changed the checksum comparing to when I use "update_checksum", but still the checksum is marked ad unverified (in Wireshark).

jafingerhut commented 4 years ago

If Wireshark says "unverified" most likely that is because you have not enabled Wireshark to ever check TCP checksums, i.e. "unverified" means "it might be correct, or it might be incorrect. I (Wireshark) have been configured not to check it."

I do not know if the GUI has changed much between different versions of Wireshark, but at least with version 2.6.10 installed on a Linux system of mine, I can change it using these steps in the GUI:

There are similar options for UDP checksums and IPv4 checksums, which you must find those protocols in the long list of protocols, and click on them, to reveal the check boxes to enable/disable those options.

sarashakeri commented 4 years ago

Thanks for the reply. I enabled Wireshark to check TCP checksum, and now it says "Checksum: 0x3509 incorrect, should be 0x3609(maybe caused by "TCP checksum offload"?)", the thing is that just the second 4 bit of the checksum is incorrect in all packets. Any idea about it?

Checksum: 0x3118 incorrect, should be 0x3218(maybe caused by "TCP checksum offload"?)
Checksum: 0x2938 incorrect, should be 0x2a38(maybe caused by "TCP checksum offload"?)
Checksum: 0x18b8 incorrect, should be 0x19b8(maybe caused by "TCP checksum offload"?)

and about the offloading, the TCP checksum offloading is off in my system, so I think that is not the reason.

jafingerhut commented 4 years ago

I would recommend that you very carefully check the order of fields that you provided to the update_checksum_verify_payload call against what is in RFC 793.

hesingh commented 4 years ago

After hdr.tcp.window, there is a missing hdr.checksum which is a zeroed out value. This is the statement from RFC 793:

While computing the checksum, the checksum field itself is replaced with zeros.

jafingerhut commented 4 years ago

That isn't the problem, I am nearly certain. Including or leaving out a 16-bit aligned 16-bit word of 0s should have absolutely no effect on the checksum value calculated.

jafingerhut commented 4 years ago

And I should add that this entire conversation isn't really an issue with the p4c compiler, so would be better if it took place elsewhere, e.g. the p4-dev email list http://lists.p4.org/mailman/listinfo/p4-dev_lists.p4.org

or the P4 Slack channels, which you can join by going to https://p4.org then click on Community near the top right, and in the menu that appears select "Slack".

sarashakeri commented 4 years ago

I checked everything and still no success. Sure, I'll ask my question somewhere more related. Thanks for all your help.

hesingh commented 4 years ago

See https://stackoverflow.com/questions/14410128/how-to-verify-tcp-checksum

hesingh commented 4 years ago

@sarashakeri Then, please close this issue.

dhawaskar commented 2 years ago

Hi, Not sure if this problem is solved. But here is how I got it working. Yes the order of the ipv4 contents is important,if this order changes then csum will differ.

Corrected solution: update_checksum_with_payload( hdr.tcp.isValid(), { hdr.ipv4.src_addr, hdr.ipv4.dst_addr, 8w0, hdr.ipv4.protocol, meta.tcpLength, hdr.tcp.src, hdr.tcp.dst, hdr.tcp.seq, hdr.tcp.ack, hdr.tcp.offset, hdr.tcp.resrv, hdr.tcp.ecn, hdr.tcp.ctrl, hdr.tcp.window, hdr.tcp.urgent, }, hdr.tcp.checksum, HashAlgorithm.csum16);

Hence it was not working for you above and you need to include the payload as well.

-sandesh