#define ICMP_TYPE_ECHO_REPLY    0
#define ICMP_TYPE_ECHO_REQUEST  8
#define ICMP_CODE_ECHO                  0 // RFC 792: "Echo or Echo Reply Message". 0 is the only code explicitly defined for Echo.

class CICMPHeader
{
        U8      type;
        U8      code;
        U16 checksum;

        U16 identifier;
        U16 sequence_number;
};


U0 ICMPReplySend(U32 destination_ip_address,
                                  U16 identifier,
                                  U16 sequence_number,
                                  U16 request_checksum,
                                  U8 *payload,
                                  I64 length)
{
        U8                      *icmp_frame;
        I64                      de_index;
        CICMPHeader     *header;

        de_index = IPV4PacketAllocate(&icmp_frame,
                                                                  IP_PROTOCOL_ICMP,
                                                                  IPV4AddressGet,
                                                                  destination_ip_address,
                                                                  sizeof(CICMPHeader) + length);
        if (de_index < 0)
        {
                NetErr("ICMP SEND REPLY: Failed to allocate IPV4 packet.");
                return;
        }

        header = icmp_frame;

        header->type                            = ICMP_TYPE_ECHO_REPLY;
        header->code                            = 0;    // why is 0 okay?
        header->checksum                        = EndianU16(EndianU16(request_checksum) + 0x0800);
        header->identifier                      = identifier;
        header->sequence_number         = sequence_number;
        // TODO: header checksum is awful. Shrine says hack alert.

        MemCopy(icmp_frame + sizeof(CICMPHeader), payload, length);

        IPV4PacketFinish(de_index);
}

I64 ICMPHandler(CIPV4Packet *packet)
{
        CICMPHeader *header;

        if (packet->length < sizeof(CICMPHeader))
        {
                NetErr("ICMP HANDLER: Caught wrong IPV4 length.");
                return -1;
        }

        header = packet->data;

        if (header->type == ICMP_TYPE_ECHO_REQUEST && header->code == ICMP_CODE_ECHO)
        {
                ARPCachePut(packet->source_ip_address, packet->ethernet_frame->source_address);

                ICMPReplySend(packet->source_ip_address,
                                          header->identifier,
                                          header->sequence_number,
                                          header->checksum,
                                          packet->data   + sizeof(CICMPHeader), // Data payload at IPV4Packet data location after the ICMP header
                                          packet->length - sizeof(CICMPHeader));// Payload length is size of packet after dropping header.
        }
        else
                NetWarn("ICMP HANDLER: Unhandled ICMP packet. type, code: 0x%X, 0x%X", header->type, header->code);

        NetLog("ICMP HANDLER: Exiting.");

        return 0;
}