Checksum calculation when adding payload to syn packet

Hello, In my P4 code I am adding some information to the Syn packet of every TCP connection. It works and I receive the packets with added information. However, the checksum is incorrect.

I know by adding new information (to the payload) of the TCP Syn packet, IP TotalLength and TCP length should be updated for recalculating the checksum. However, still my code does not work! Here is the code:

header ethernet_t {
    macAddr_t dstAddr;
    macAddr_t srcAddr;
    bit<16>   etherType;
}

header ipv4_t {
    bit<4>    version;
    bit<4>    ihl;
    bit<8>    diffserv;
    bit<16>   totalLen;
    bit<16>   identification;
    bit<3>    flags;
    bit<13>   fragOffset;
    bit<8>    ttl;
    bit<8>    protocol;
    bit<16>   hdrChecksum;
    ip4Addr_t srcAddr;
    ip4Addr_t dstAddr;
    varbit<320>  options;
}
header tcp_t {
    bit<16> srcPort;
    bit<16> dstPort;
    bit<32> seqNo;
    bit<32> ackNo;
    bit<4>  dataOffset;
    bit<4>  res;
    bit<1>  cwr;
    bit<1>  ecn;
    bit<1>  urg;
    bit<1>  ack;
    bit<1>  psh;
    bit<1>  rst;
    bit<1>  syn;
    bit<1>  fin;
    bit<16> window;
    bit<16> checksum; // Includes Pseudo Hdr + TCP segment (hdr + payload)
    bit<16> urgentPtr;
    varbit<320>  options;
}
header udp_t {
    bit<16> srcPort;
    bit<16> dstPort;
    bit<16> length_;
    bit<16> checksum;
}

header myTunnel_t {
    bit<16> proto_id;
    bit<16> dst_id;
}
header IPv4_up_to_ihl_only_h {
    bit<4>       version;
    bit<4>       ihl;
}
header tcp_upto_data_offset_only_h {
    bit<16> srcPort;
    bit<16> dstPort;
    bit<32> seqNo;
    bit<32> ackNo;
    // dataOffset in TCP hdr uses 4 bits but needs padding.
    // If 4 bits are used for it, p4c-bm2-ss complains the header
    // is not a multiple of 8 bits.
    bit<4>  dataOffset;
    bit<4>  dontCare;
}
struct headers {
    ethernet_t   ethernet;
    ipv4_t       ipv4;
    tcp_t        tcp;
    myTunnel_t   my_tunnel;
}

struct metadata {
    mystruct1_t mystruct1;
    bit<16>     l4Len; // includes TCP hdr len + TCP payload len in bytes.
}

Adding fields and updating length of the packet:

action syn_client_send (bit<48> src_mac, bit<48> dst_mac, bit<32> src_ip, bit<9> port){
    ........
	hdr.my_tunnel.setValid();
        hdr.my_tunnel.proto_id=6;
        hdr.my_tunnel.dst_id=3;
	hdr.ipv4.totalLen=hdr.ipv4.totalLen + TUNNEL_HDR_SIZE; //Updating headerLen cause we are adding hdr.my_tunnel.
        meta.l4Len = hdr.ipv4.totalLen - (bit<16>)(hdr.ipv4.ihl)*4;

Checksum calculation:

control MyComputeChecksum(inout headers  hdr, inout metadata meta) {
     apply {
	update_checksum(
	    hdr.ipv4.isValid(),
            { hdr.ipv4.version,
	      hdr.ipv4.ihl,
              hdr.ipv4.diffserv,
              hdr.ipv4.totalLen,
              hdr.ipv4.identification,
              hdr.ipv4.flags,
              hdr.ipv4.fragOffset,
              hdr.ipv4.ttl,
              hdr.ipv4.protocol,
              hdr.ipv4.srcAddr,
              hdr.ipv4.dstAddr },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16);
	     update_checksum_with_payload(hdr.tcp.isValid(),
            { hdr.ipv4.srcAddr,
                hdr.ipv4.dstAddr,
                8w0,
                hdr.ipv4.protocol,
                meta.l4Len,
                hdr.tcp.srcPort,
                hdr.tcp.dstPort,
                hdr.tcp.seqNo,
                hdr.tcp.ackNo,
                hdr.tcp.dataOffset,
                hdr.tcp.res,
                hdr.tcp.ecn,
                hdr.tcp.urg,
                hdr.tcp.ack,
                hdr.tcp.psh,
                hdr.tcp.rst,
                hdr.tcp.syn,
                hdr.tcp.fin,
                hdr.tcp.window,
                16w0,
                hdr.tcp.urgentPtr,
                hdr.tcp.options
            },
            hdr.tcp.checksum, HashAlgorithm.csum16);      
    }
}


Deparsing the packet:

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
        packet.emit(hdr.tcp);
        packet.emit(hdr.my_tunnel);
    }
}

Hi Sara,

This is Eder from Slack. I am checking again your questionand I have a some comments.

  • Have you tried removing the varbit<320> option field (for now)? If you don’t need the options for anything I recommend that you don’t parse them. It should ease the process. This is because I am not sure how varbits behave when it comes to calculating the checksum. If you still need the options, I would recommend defining exactly which options you parse (therefore create specific headers for them) in your test case and then feed the function with those “static” option headers. In this way, you could be sure that varbits are not causing the issue. In summary, I would remove the option field for now and try to calculate the checksum using the most simple use case, which is with the typical TCP fields (no options) and the mytunnel_t fields (see next point).

  • Also, compared to yesterday I see that you now add a mytunnel_t header. As far as I understand :thinking:, this should also be put into the checksum calculation function because mytunnel_t is payload too as long as the TCP header is concerned. Since you are setting it valid, I guess you have to feed the function with the mytunnel_t fields too.

  • Be sure that TUNNEL_HDR_SIZE is 12 4 bytes.

Let’s see if that works :slightly_smiling_face:

PD: If anyone knows more about this topic or if I was wrong in anything please comment :fist_right: :fist_left:

Cheers,

Hello ederollora,
Thanks for your reply.
As you recommended, I removed the TCP option but still, the checksum is not correct, and I am sure that TUNNEL_HDR_SIZE is 4.
How can I put mytunnel_t into checksum calculation? I just change the meta.l4Len when adding the new header, and it is being used in tcp checksum calculation function. I am wondering what other items should I take into account in checksum function when using mytunnel header?

Hi Sara,

I found your error. I think it works for me let’s see if you have the same error.

The main mistake I found is that you miss a field. This one:

bit<1>  cwr;

So your function should be:

(...)
hdr.tcp.res,
hdr.tcp.cwr, //missing field
hdr.tcp.ecn,
(...)

Now, if you want to add my_tunnel to your packet, you have to run the following checksum function:

//In your action
    hdr.ipv4.totalLen=hdr.ipv4.totalLen + TUNNEL_HDR_SIZE;
    meta.tcpLength = hdr.ipv4.totalLen - (bit<16>)(hdr.ipv4.ihl)*4

//In your checksum computation
    update_checksum_with_payload(hdr.tcp.isValid(),
        {   hdr.ipv4.srcAddr,
            hdr.ipv4.dstAddr,
            8w0,
            hdr.ipv4.protocol,
            meta.tcpLength, //I changed this name, so change it back to yours
            hdr.tcp.srcPort,
            hdr.tcp.dstPort,
            hdr.tcp.seqNo,
            hdr.tcp.ackNo,
            hdr.tcp.dataOffset,
            hdr.tcp.res,
            hdr.tcp.cwr,
            hdr.tcp.ecn,
            hdr.tcp.urg,
            hdr.tcp.ack,
            hdr.tcp.psh,
            hdr.tcp.rst,
            hdr.tcp.syn,
            hdr.tcp.fin,
            hdr.tcp.window,
            16w0,
            hdr.tcp.urgentPtr,
            hdr.my_tunnel.proto_id,
            hdr.my_tunnel.dst_id},
        hdr.tcp.checksum, HashAlgorithm.csum16);
    }

The problem at this point is to add TCP options. Why? you might ask. Well, a few reasons:

  1. If you use options defined as a varbit field… I have no idea how they behave. So… you will have to test the update_checksum_with_payload function again with TCP options ar varbit :slight_smile:
  2. If you do not parse TCP options then my_tunnel will remain between the TCP header and the options. If that works for you then you are fine.
  3. Also, be aware that if the packet has IPv4 options, my code does not account for that right now. If your use case can does not include options… then much better.

I remember this file from Andy Fingerhut (here) in which he defines options as “typical” without varbit fields. He shared two parsers there so check if any works for you (in one of them he uses a stack). Still, it is going to be a little challenge to calculate the TCP checksum IF you need to parse TCP options mandatorily. I will let you investigate that.

I hope it helps.

Cheers,

Hello ederollora,

Thanks a lot, it works. I had to add my_tunnel header in the checksum calculation. And, I am now running it with options varbit and it works fine with no problem.

Thanks again for your help:)

1 Like

I am happy it works :slight_smile:

Regards,