#include <gtest/gtest.h>

#include "packet_helper.h"

/******************************************************************************
 * more fragment
 ******************************************************************************/

/*
 * Internet Protocol Version 4, Src: 192.168.36.103, Dst: 192.168.40.137
 *     0100 .... = Version: 4
 *     .... 0101 = Header Length: 20 bytes (5)
 *     Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
 *         0000 00.. = Differentiated Services Codepoint: Default (0)
 *         .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
 *     Total Length: 44
 *     Identification: 0xffff (65535)
 *     001. .... = Flags: 0x1, More fragments
 *         0... .... = Reserved bit: Not set
 *         .0.. .... = Don't fragment: Not set
 *         ..1. .... = More fragments: Set
 *     ...0 0000 0000 0000 = Fragment Offset: 0
 *     Time to Live: 127
 *     Protocol: TCP (6)
 *     Header Checksum: 0x4d8b [correct]
 *     [Header checksum status: Good]
 *     [Calculated Checksum: 0x4d8b]
 *     Source Address: 192.168.36.103
 *     Destination Address: 192.168.40.137
 *     [Reassembled IPv4 in frame: 5]
 * Data (24 bytes)
 */

unsigned char data1[] = {0x45, 0x00, 0x00, 0x2c, 0xff, 0xff, 0x20, 0x00, 0x7f, 0x06, 0x4d, 0x8b, 0xc0, 0xa8, 0x24, 0x67, 0xc0, 0xa8, 0x28, 0x89};

TEST(IPV4_UTILS, GET1)
{
    const struct ip *hdr = (struct ip *)data1;
    EXPECT_TRUE(ip4_hdr_get_version(hdr) == 4);
    EXPECT_TRUE(ip4_hdr_get_hdr_len(hdr) == 20);
    EXPECT_TRUE(ip4_hdr_get_tos(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_total_len(hdr) == 44);
    EXPECT_TRUE(ip4_hdr_get_ipid(hdr) == 65535);
    EXPECT_TRUE(ip4_hdr_get_flags(hdr) == 1);
    EXPECT_TRUE(ip4_hdr_get_rf_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_df_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_mf_flag(hdr) == true);
    EXPECT_TRUE(ip4_hdr_get_frag_offset(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_ttl(hdr) == 127);
    EXPECT_TRUE(ip4_hdr_get_proto(hdr) == 6);
    EXPECT_TRUE(ip4_hdr_get_checksum(hdr) == 0x4d8b);
    EXPECT_TRUE(ip4_hdr_get_src_addr(hdr) == 0xc0a82467);
    EXPECT_TRUE(ip4_hdr_get_dst_addr(hdr) == 0xc0a82889);
    EXPECT_TRUE(ip4_hdr_get_opt_len(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_opt_data(hdr) == NULL);
}

TEST(IPV4_UTILS, SET1)
{
    char buff[20] = {0};

    struct ip *hdr = (struct ip *)buff;
    ip4_hdr_set_version(hdr, 4);
    ip4_hdr_set_hdr_len(hdr, 20);
    ip4_hdr_set_tos(hdr, 0);
    ip4_hdr_set_total_len(hdr, 44);
    ip4_hdr_set_ipid(hdr, 65535);
    ip4_hdr_set_frag_offset(hdr, 0);
    ip4_hdr_set_ttl(hdr, 127);
    ip4_hdr_set_protocol(hdr, 6);
    ip4_hdr_set_checksum(hdr, 0x4d8b);
    ip4_hdr_set_src_addr(hdr, 0xc0a82467);
    ip4_hdr_set_dst_addr(hdr, 0xc0a82889);
    ip4_hdr_set_opt_len(hdr, 0);
    ip4_hdr_set_opt_data(hdr, NULL);

    ip4_hdr_set_flags(hdr, 1);
    EXPECT_TRUE(memcmp(buff, data1, 20) == 0);

    ip4_hdr_set_rf_flag(hdr, false);
    ip4_hdr_set_df_flag(hdr, false);
    ip4_hdr_set_mf_flag(hdr, true);
    EXPECT_TRUE(memcmp(buff, data1, 20) == 0);
}

/******************************************************************************
 * fragment offset
 ******************************************************************************/

/*
 * Frame 5: 60 bytes on wire (480 bits), 60 bytes captured (480 bits)
 * Ethernet II, Src: Fortinet_cc:87:22 (e8:1c:ba:cc:87:22), Dst: EvocInte_2f:35:b8 (00:22:46:2f:35:b8)
 * Internet Protocol Version 4, Src: 192.168.36.103, Dst: 192.168.40.137
 *     0100 .... = Version: 4
 *     .... 0101 = Header Length: 20 bytes (5)
 *     Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
 *         0000 00.. = Differentiated Services Codepoint: Default (0)
 *         .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
 *     Total Length: 44
 *     Identification: 0xffff (65535)
 *     000. .... = Flags: 0x0
 *         0... .... = Reserved bit: Not set
 *         .0.. .... = Don't fragment: Not set
 *         ..0. .... = More fragments: Not set
 *     ...0 0000 0000 0011 = Fragment Offset: 24
 *     Time to Live: 127
 *     Protocol: TCP (6)
 *     Header Checksum: 0x6d88 [correct]
 *     [Header checksum status: Good]
 *     [Calculated Checksum: 0x6d88]
 *     Source Address: 192.168.36.103
 *     Destination Address: 192.168.40.137
 *     [2 IPv4 Fragments (48 bytes): #4(24), #5(24)]
 *         [Frame: 4, payload: 0-23 (24 bytes)]
 *         [Frame: 5, payload: 24-47 (24 bytes)]
 *         [Fragment count: 2]
 *         [Reassembled IPv4 length: 48]
 *         [Reassembled IPv4 data: f4a5270f9107248703d518e75018ff005e9200003132330af4a5270f9107248b03d518e7…]
 * Transmission Control Protocol, Src Port: 62629, Dst Port: 9999, Seq: 1, Ack: 1, Len: 28
 * Data (28 bytes)
 *     Data: 3132330af4a5270f9107248b03d518e75018ff00301600006162630a
 *     [Length: 28]
 */

unsigned char data2[] = {0x45, 0x00, 0x00, 0x2c, 0xff, 0xff, 0x00, 0x03, 0x7f, 0x06, 0x6d, 0x88, 0xc0, 0xa8, 0x24, 0x67, 0xc0, 0xa8, 0x28, 0x89};

TEST(IPV4_UTILS, GET2)
{
    const struct ip *hdr = (struct ip *)data2;
    EXPECT_TRUE(ip4_hdr_get_version(hdr) == 4);
    EXPECT_TRUE(ip4_hdr_get_hdr_len(hdr) == 20);
    EXPECT_TRUE(ip4_hdr_get_tos(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_total_len(hdr) == 44);
    EXPECT_TRUE(ip4_hdr_get_ipid(hdr) == 65535);
    EXPECT_TRUE(ip4_hdr_get_flags(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_rf_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_df_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_mf_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_frag_offset(hdr) == 24);
    EXPECT_TRUE(ip4_hdr_get_ttl(hdr) == 127);
    EXPECT_TRUE(ip4_hdr_get_proto(hdr) == 6);
    EXPECT_TRUE(ip4_hdr_get_checksum(hdr) == 0x6d88);
    EXPECT_TRUE(ip4_hdr_get_src_addr(hdr) == 0xc0a82467);
    EXPECT_TRUE(ip4_hdr_get_dst_addr(hdr) == 0xc0a82889);
    EXPECT_TRUE(ip4_hdr_get_opt_len(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_opt_data(hdr) == NULL);
}

TEST(IPV4_UTILS, SET2)
{
    char buff[20] = {0};

    struct ip *hdr = (struct ip *)buff;
    ip4_hdr_set_version(hdr, 4);
    ip4_hdr_set_hdr_len(hdr, 20);
    ip4_hdr_set_tos(hdr, 0);
    ip4_hdr_set_total_len(hdr, 44);
    ip4_hdr_set_ipid(hdr, 65535);
    ip4_hdr_set_frag_offset(hdr, 24);
    ip4_hdr_set_ttl(hdr, 127);
    ip4_hdr_set_protocol(hdr, 6);
    ip4_hdr_set_checksum(hdr, 0x6d88);
    ip4_hdr_set_src_addr(hdr, 0xc0a82467);
    ip4_hdr_set_dst_addr(hdr, 0xc0a82889);
    ip4_hdr_set_opt_len(hdr, 0);
    ip4_hdr_set_opt_data(hdr, NULL);

    ip4_hdr_set_flags(hdr, 0);
    EXPECT_TRUE(memcmp(buff, data2, 20) == 0);

    ip4_hdr_set_rf_flag(hdr, false);
    ip4_hdr_set_df_flag(hdr, false);
    ip4_hdr_set_mf_flag(hdr, false);
    EXPECT_TRUE(memcmp(buff, data2, 20) == 0);
}

/******************************************************************************
 * options
 ******************************************************************************/

/*
 * Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
 *     0100 .... = Version: 4
 *     .... 1111 = Header Length: 60 bytes (15)
 *     Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
 *         0000 00.. = Differentiated Services Codepoint: Default (0)
 *         .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
 *     Total Length: 124
 *     Identification: 0x0000 (0)
 *     010. .... = Flags: 0x2, Don't fragment
 *         0... .... = Reserved bit: Not set
 *         .1.. .... = Don't fragment: Set
 *         ..0. .... = More fragments: Not set
 *     ...0 0000 0000 0000 = Fragment Offset: 0
 *     Time to Live: 64
 *     Protocol: ICMP (1)
 *     Header Checksum: 0xfd30 [correct]
 *     [Header checksum status: Good]
 *     [Calculated Checksum: 0xfd30]
 *     Source Address: 127.0.0.1
 *     Destination Address: 127.0.0.1
 *     Options: (40 bytes), Commercial Security
 *         IP Option - Commercial Security (40 bytes)
 *             Type: 134
 *                 1... .... = Copy on fragmentation: Yes
 *                 .00. .... = Class: Control (0)
 *                 ...0 0110 = Number: Commercial IP security option (6)
 *             Length: 40
 *             DOI: 1
 *             Tag Type: Restrictive Category Bitmap (1)
 *             Sensitivity Level: 1
 *             Categories: 0,2,4,5,6,239
 */

unsigned char data3[] = {
    0x4f, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xfd, 0x30, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x86, 0x28, 0x00, 0x00, 0x00, 0x01,
    0x01, 0x22, 0x00, 0x01, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};

TEST(IPV4_UTILS, GET3)
{
    const struct ip *hdr = (struct ip *)data3;
    EXPECT_TRUE(ip4_hdr_get_version(hdr) == 4);
    EXPECT_TRUE(ip4_hdr_get_hdr_len(hdr) == 60);
    EXPECT_TRUE(ip4_hdr_get_tos(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_total_len(hdr) == 124);
    EXPECT_TRUE(ip4_hdr_get_ipid(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_flags(hdr) == 2);
    EXPECT_TRUE(ip4_hdr_get_rf_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_df_flag(hdr) == true);
    EXPECT_TRUE(ip4_hdr_get_mf_flag(hdr) == false);
    EXPECT_TRUE(ip4_hdr_get_frag_offset(hdr) == 0);
    EXPECT_TRUE(ip4_hdr_get_ttl(hdr) == 64);
    EXPECT_TRUE(ip4_hdr_get_proto(hdr) == 1);
    EXPECT_TRUE(ip4_hdr_get_checksum(hdr) == 0xfd30);
    EXPECT_TRUE(ip4_hdr_get_src_addr(hdr) == 0x7f000001);
    EXPECT_TRUE(ip4_hdr_get_dst_addr(hdr) == 0x7f000001);
    EXPECT_TRUE(ip4_hdr_get_opt_len(hdr) == 40);
    EXPECT_TRUE(ip4_hdr_get_opt_data(hdr) == (const char *)(data3 + 20));
}

TEST(IPV4_UTILS, SET3)
{
    char buff[60] = {0};

    struct ip *hdr = (struct ip *)buff;
    ip4_hdr_set_version(hdr, 4);
    ip4_hdr_set_hdr_len(hdr, 60);
    ip4_hdr_set_tos(hdr, 0);
    ip4_hdr_set_total_len(hdr, 124);
    ip4_hdr_set_ipid(hdr, 0);
    ip4_hdr_set_frag_offset(hdr, 0);
    ip4_hdr_set_ttl(hdr, 64);
    ip4_hdr_set_protocol(hdr, 1);
    ip4_hdr_set_checksum(hdr, 0xfd30);
    ip4_hdr_set_src_addr(hdr, 0x7f000001);
    ip4_hdr_set_dst_addr(hdr, 0x7f000001);
    ip4_hdr_set_opt_len(hdr, 40);
    ip4_hdr_set_opt_data(hdr, (const char *)(data3 + 20));

    ip4_hdr_set_flags(hdr, 2);
    EXPECT_TRUE(memcmp(buff, data3, 60) == 0);

    ip4_hdr_set_rf_flag(hdr, false);
    ip4_hdr_set_df_flag(hdr, true);
    ip4_hdr_set_mf_flag(hdr, false);
    EXPECT_TRUE(memcmp(buff, data3, 60) == 0);
}

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
