/* -*- P4_16 -*- */
#ifndef CHECKSUMS_P4
#define CHECKSUMS_P4

#include <core.p4>
#include <v1model.p4>

#include "headers.p4"

/*************************************************************************
*************   C H E C K S U M    V E R I F I C A T I O N   *************
*************************************************************************/

control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
    apply {}
}

/*************************************************************************
**************   C H E C K S U M    C O M P U T A T I O N   **************
*************************************************************************/

control MyComputeChecksum(inout headers  hdr, inout metadata meta) {
    apply {
        update_checksum_with_payload(meta.chk_icmp6 == 1,
		 	{
                hdr.ipv6.src_addr,         /* 128 */
		 		hdr.ipv6.dst_addr,         /* 128 */
                meta.cast_length,          /* 32 */
                24w0,                      /* 24 0's */
                PROTO_ICMP6,               /* 8 */
                hdr.icmp6.type,            /* 8 */
                hdr.icmp6.code             /* 8 */
            },
            hdr.icmp6.checksum,
            HashAlgorithm.csum16
        );

        /* checksumming for icmp6_na_ns_option */
        update_checksum_with_payload(meta.chk_icmp6_na_ns == 1,
		 	{
                hdr.ipv6.src_addr,         /* 128 */
		 		hdr.ipv6.dst_addr,         /* 128 */
                meta.cast_length,          /* 32 */
                24w0,                      /* 24 0's */
                PROTO_ICMP6,               /* 8 */
                hdr.icmp6.type,            /* 8 */
                hdr.icmp6.code,            /* 8 */

                hdr.icmp6_na_ns.router,
                hdr.icmp6_na_ns.solicitated,
                hdr.icmp6_na_ns.override,
                hdr.icmp6_na_ns.reserved,
                hdr.icmp6_na_ns.target_addr,

                hdr.icmp6_option_link_layer_addr.type,
                hdr.icmp6_option_link_layer_addr.ll_length,
                hdr.icmp6_option_link_layer_addr.mac_addr
            },
            hdr.icmp6.checksum,
            HashAlgorithm.csum16
        );

        update_checksum_with_payload(meta.chk_icmp == 1,
		 	{
                hdr.icmp.type,
                hdr.icmp.code
            },
            hdr.icmp.checksum,
            HashAlgorithm.csum16
        );

	    update_checksum(meta.chk_ipv4 == 1,
            {
                hdr.ipv4.version,
	            hdr.ipv4.ihl,
                hdr.ipv4.diff_serv,
                hdr.ipv4.ecn,
                hdr.ipv4.totalLen,
                hdr.ipv4.identification,
                hdr.ipv4.flags,
                hdr.ipv4.fragOffset,
                hdr.ipv4.ttl,
                hdr.ipv4.protocol,
                hdr.ipv4.src_addr,
                hdr.ipv4.dst_addr
            },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16
        );

	    update_checksum_with_payload(meta.chk_udp_v4 == 1,
            {
                hdr.ipv4.src_addr,
                hdr.ipv4.dst_addr,
                8w0,
                hdr.ipv4.protocol,
			    meta.length_without_ip_header,

				// UDP header
                hdr.udp.src_port,
                hdr.udp.dst_port,
                hdr.udp.payload_length
            },
            hdr.udp.checksum,
            HashAlgorithm.csum16
        );

	    update_checksum_with_payload(meta.chk_udp_v6 == 1,
            {
                hdr.ipv6.src_addr,               /* 128 */
                hdr.ipv6.dst_addr,               /* 128 */
			    meta.length_without_ip_header,   /* 32 */
                24w0,                            /* 24 */
				hdr.ipv6.next_header,            /* 8 */
                                                 /* total: 324 */

				// UDP header
                hdr.udp.src_port,                /* 16 */
                hdr.udp.dst_port,                /* 16 */
                hdr.udp.payload_length           /* 16 */
                                                 /* all: 372 */

            },
            hdr.udp.checksum,
            HashAlgorithm.csum16
        );

		update_checksum_with_payload(meta.chk_tcp_v4 == 1,
			{
                hdr.ipv4.src_addr,
				hdr.ipv4.dst_addr,
				8w0,
				hdr.ipv4.protocol,
                meta.length_without_ip_header,

				// TCP header
				hdr.tcp.src_port,
				hdr.tcp.dst_port,
				hdr.tcp.seqNo,
				hdr.tcp.ackNo,
				hdr.tcp.data_offset,
				hdr.tcp.res,
				hdr.tcp.cwr,
				hdr.tcp.ece,
				hdr.tcp.urg,
				hdr.tcp.ack,
				hdr.tcp.psh,
				hdr.tcp.rst,
				hdr.tcp.syn,
				hdr.tcp.fin,
				hdr.tcp.window,
				hdr.tcp.urgentPtr
			},
			hdr.tcp.checksum,
			HashAlgorithm.csum16
        );

		update_checksum_with_payload(meta.chk_tcp_v6 == 1,
			{
                hdr.ipv6.src_addr,
				hdr.ipv6.dst_addr,
			    meta.length_without_ip_header,
				24w0,
				hdr.ipv6.next_header,

				// TCP header
				hdr.tcp.src_port,
				hdr.tcp.dst_port,
				hdr.tcp.seqNo,
				hdr.tcp.ackNo,
				hdr.tcp.data_offset,
				hdr.tcp.res,
				hdr.tcp.cwr,
				hdr.tcp.ece,
				hdr.tcp.urg,
				hdr.tcp.ack,
				hdr.tcp.psh,
				hdr.tcp.rst,
				hdr.tcp.syn,
				hdr.tcp.fin,
				hdr.tcp.window,
				hdr.tcp.urgentPtr
			},
			hdr.tcp.checksum,
			HashAlgorithm.csum16);

    }
}


#endif
