#include <gtest/gtest.h>

#include "packet_helper.h"

/*
 * User Datagram Protocol, Src Port: 2123, Dst Port: 2123
 * GPRS Tunneling Protocol
 *     Flags: 0x32
 *         001. .... = Version: GTP release 99 version (1)
 *         ...1 .... = Protocol type: GTP (1)
 *         .... 0... = Reserved: 0
 *         .... .0.. = Is Next Extension Header present?: No
 *         .... ..1. = Is Sequence Number present?: Yes
 *         .... ...0 = Is N-PDU number present?: No
 *     Message Type: Create PDP context request (0x10)
 *     Length: 151
 *     TEID: 0x00000000 (0)
 *     Sequence number: 0x00fe (254)
 *     IMSI: 27203
 *     [Association IMSI: 27203]
 *         Mobile Country Code (MCC): Ireland (272)
 *         Mobile Network Code (MNC): Eircom Ltd (03)
 *     Routing Area Identity
 *         Mobile Country Code (MCC): Ireland (272)
 *         Mobile Network Code (MNC): Eircom Ltd (03)
 *         Location Area Code (LAC): 65534
 *         Routing Area Code (RAC): 255
 *     Recovery: 93
 *     Selection mode: MS or network provided APN, subscribed verified
 *         .... ..00 = Selection mode: MS or network provided APN, subscribed verified (0)
 *     TEID Data I: 0x372f0000 (925827072)
 *     TEID Control Plane: 0x372f0000 (925827072)
 *     NSAPI: 5
 *         .... 0101 = NSAPI: 5
 *     End user address (IETF/IPv4)
 *         Length: 2
 *         PDP type organization: IETF (1)
 *         PDP type number: IPv4 (0x21)
 *     Access Point Name: mms.mymeteor.ie
 *         APN length: 16
 *         APN: mms.mymeteor.ie
 *     Protocol configuration options
 *         Length: 34
 *         [Link direction: MS to network (0)]
 *         1... .... = Extension: True
 *         .... .000 = Configuration Protocol: PPP for use with IP PDP type or IP PDN type (0)
 *         Protocol or Container ID: Password Authentication Protocol (0xc023)
 *             Length: 0x0b (11)
 *             PPP Password Authentication Protocol
 *                 Code: Authenticate-Request (1)
 *                 Identifier: 0
 *                 Length: 11
 *                 Data
 *                     Peer-ID-Length: 2
 *                     Peer-ID: my
 *                     Password-Length: 3
 *                     Password: wap
 *         Protocol or Container ID: Internet Protocol Control Protocol (0x8021)
 *             Length: 0x10 (16)
 *             PPP IP Control Protocol
 *                 Code: Configuration Request (1)
 *                 Identifier: 0 (0x00)
 *                 Length: 16
 *                 Options: (12 bytes), Primary DNS Server IP Address, Secondary DNS Server IP Address
 *                     Primary DNS Server IP Address
 *                         Type: Primary DNS Server IP Address (129)
 *                         Length: 6
 *                         Primary DNS Address: 0.0.0.0
 *                     Secondary DNS Server IP Address
 *                         Type: Secondary DNS Server IP Address (131)
 *                         Length: 6
 *                         Secondary DNS Address: 0.0.0.0
 *     GSN address : 212.129.65.13
 *         GSN address length: 4
 *         GSN address IPv4: 212.129.65.13
 *     GSN address : 212.129.65.23
 *         GSN address length: 4
 *         GSN address IPv4: 212.129.65.23
 *     MS international PSTN/ISDN number
 *         Length: 7
 *         1... .... = Extension: No Extension
 *         .001 .... = Nature of number: International Number (0x1)
 *         .... 0001 = Number plan: ISDN/Telephony Numbering (Rec ITU-T E.164) (0x1)
 *         E.164 number (MSISDN): 353800000000
 *             Country Code: Ireland (353)
 *     Quality of Service
 *         Length: 12
 *         Allocation/Retention priority: 2
 *         00.. .... = Spare: 0
 *         ..10 0... = QoS delay: Delay class 4 (best effort) (4)
 *         .... .011 = QoS reliability: Unacknowledged GTP/LLC, Ack RLC, Protected data (3)
 *         0110 .... = QoS peak: Up to 32 000 oct/s (6)
 *         .... 0... = Spare: 0
 *         .... .010 = QoS precedence: Normal priority (2)
 *         000. .... = Spare: 0
 *         ...1 1111 = QoS mean: Best effort (31)
 *         100. .... = Traffic class: Background class (4)
 *         ...1 0... = Delivery order: Without delivery order ('no') (2)
 *         .... .011 = Delivery of erroneous SDU: Erroneous SDUs are not delivered ('no') (3)
 *         Maximum SDU size: 1500 octets
 *         Maximum bit rate for uplink: 256 kbps
 *         Maximum bit rate for downlink: 256 kbps
 *         0111 .... = Residual BER: 1/100 000 = 1x10^-5 (7)
 *         .... 0100 = SDU Error ratio: 1/10 000 = 1x10^-4 (4)
 *         1111 10.. = Transfer delay: 4000 ms (62)
 *         .... ..11 = Traffic handling priority: Priority level 3 (3)
 *         Guaranteed bit rate for uplink: 0 kbps (255)
 *         Guaranteed bit rate for downlink: 0 kbps (255)
 *     RAT Type: GERAN
 *         Length: 1
 *         RAT Type: GERAN (2)
 *     IMEI(SV): 3598100185893512
 *         Length: 8
 *         IMEI(SV): 3598100185893512
 *     [Response In: 2]
 */

unsigned char gtp1_c[] = {
    0x32, 0x10, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x02, 0x72, 0x02, 0xf3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x72, 0xf2, 0x30, 0xff,
    0xfe, 0xff, 0x0e, 0x5d, 0x0f, 0xfc, 0x10, 0x37, 0x2f, 0x00, 0x00, 0x11, 0x37, 0x2f, 0x00, 0x00, 0x14, 0x05, 0x80, 0x00, 0x02, 0xf1, 0x21, 0x83, 0x00, 0x10,
    0x03, 0x6d, 0x6d, 0x73, 0x08, 0x6d, 0x79, 0x6d, 0x65, 0x74, 0x65, 0x6f, 0x72, 0x02, 0x69, 0x65, 0x84, 0x00, 0x22, 0x80, 0xc0, 0x23, 0x0b, 0x01, 0x00, 0x00,
    0x0b, 0x02, 0x6d, 0x79, 0x03, 0x77, 0x61, 0x70, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00, 0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00, 0x00,
    0x00, 0x85, 0x00, 0x04, 0xd4, 0x81, 0x41, 0x0d, 0x85, 0x00, 0x04, 0xd4, 0x81, 0x41, 0x17, 0x86, 0x00, 0x07, 0x91, 0x53, 0x83, 0x00, 0x00, 0x00, 0x00, 0x87,
    0x00, 0x0c, 0x02, 0x23, 0x62, 0x1f, 0x93, 0x96, 0x58, 0x58, 0x74, 0xfb, 0xff, 0xff, 0x97, 0x00, 0x01, 0x02, 0x9a, 0x00, 0x08, 0x53, 0x89, 0x01, 0x10, 0x58,
    0x98, 0x53, 0x21};

TEST(GTP1_UTILS, C_GET)
{
    const struct gtp1_hdr *hdr = (struct gtp1_hdr *)gtp1_c;

    // GTP Fixed Header Length + GTPv1-C Optional Headers + GTPv1-C Extension Headers
    EXPECT_TRUE(calc_gtp1_hdr_len((const char *)gtp1_c, sizeof(gtp1_c)) == 12);
    EXPECT_TRUE(gtp1_hdr_get_flags(hdr) == 0x32);
    EXPECT_TRUE(gtp1_hdr_get_version(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_proto(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_reserved(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_ext_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_seq_flag(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_npdu_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_msg_type(hdr) == 0x10);
    EXPECT_TRUE(gtp1_hdr_get_msg_len(hdr) == 151);
    EXPECT_TRUE(gtp1_hdr_get_teid(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_seq(hdr) == 254);
    EXPECT_TRUE(gtp1_hdr_get_npdu(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_next_ext_type(hdr) == 0);

    char buff[1024] = {0};
    gtp1_hdr_to_str(hdr, buff, sizeof(buff));
    printf("%s\n", buff);
}

TEST(GTP1_UTILS, C_SET)
{
    char buff[12] = {0};

    struct gtp1_hdr *hdr = (struct gtp1_hdr *)buff;
    gtp1_hdr_set_flags(hdr, 0x32);
    gtp1_hdr_set_version(hdr, 1);
    gtp1_hdr_set_proto(hdr, 1);
    gtp1_hdr_set_reserved(hdr, 0);
    gtp1_hdr_set_ext_flag(hdr, 0);
    gtp1_hdr_set_seq_flag(hdr, 1);
    gtp1_hdr_set_npdu_flag(hdr, 0);
    gtp1_hdr_set_msg_type(hdr, 0x10);
    gtp1_hdr_set_msg_len(hdr, 151);
    gtp1_hdr_set_teid(hdr, 0);
    gtp1_hdr_set_seq(hdr, 254);
    gtp1_hdr_set_npdu(hdr, 0);
    gtp1_hdr_set_next_ext_type(hdr, 0);

    EXPECT_TRUE(memcmp(buff, gtp1_c, 12) == 0);
}

/*
 * User Datagram Protocol, Src Port: 2152, Dst Port: 2152
 * GPRS Tunneling Protocol
 *     Flags: 0x30
 *         001. .... = Version: GTP release 99 version (1)
 *         ...1 .... = Protocol type: GTP (1)
 *         .... 0... = Reserved: 0
 *         .... .0.. = Is Next Extension Header present?: No
 *         .... ..0. = Is Sequence Number present?: No
 *         .... ...0 = Is N-PDU number present?: No
 *     Message Type: T-PDU (0xff)
 *     Length: 40
 *     TEID: 0x001e849a (2000026)
 */

unsigned char gtp1_u1[] = {
    0x30, 0xff, 0x00, 0x28, 0x00, 0x1e, 0x84, 0x9a};

TEST(GTP1_UTILS, U1_GET)
{
    const struct gtp1_hdr *hdr = (struct gtp1_hdr *)gtp1_u1;

    // GTP Fixed Header Length + GTPv1-U Optional Headers + GTPv1-U Extension Headers
    EXPECT_TRUE(calc_gtp1_hdr_len((const char *)gtp1_u1, sizeof(gtp1_u1)) == 8);
    EXPECT_TRUE(gtp1_hdr_get_flags(hdr) == 0x30);
    EXPECT_TRUE(gtp1_hdr_get_version(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_proto(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_reserved(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_ext_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_seq_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_npdu_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_msg_type(hdr) == 0xff);
    EXPECT_TRUE(gtp1_hdr_get_msg_len(hdr) == 40);
    EXPECT_TRUE(gtp1_hdr_get_teid(hdr) == 2000026);
    EXPECT_TRUE(gtp1_hdr_get_seq(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_npdu(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_next_ext_type(hdr) == 0);

    char buff[1024] = {0};
    gtp1_hdr_to_str(hdr, buff, sizeof(buff));
    printf("%s\n", buff);
}

TEST(GTP1_UTILS, U1_SET)
{
    char buff[8] = {0};

    struct gtp1_hdr *hdr = (struct gtp1_hdr *)buff;
    gtp1_hdr_set_flags(hdr, 0x30);
    gtp1_hdr_set_version(hdr, 1);
    gtp1_hdr_set_proto(hdr, 1);
    gtp1_hdr_set_reserved(hdr, 0);
    gtp1_hdr_set_ext_flag(hdr, 0);
    gtp1_hdr_set_seq_flag(hdr, 0);
    gtp1_hdr_set_npdu_flag(hdr, 0);
    gtp1_hdr_set_msg_type(hdr, 0xff);
    gtp1_hdr_set_msg_len(hdr, 40);
    gtp1_hdr_set_teid(hdr, 2000026);
    gtp1_hdr_set_seq(hdr, 0);
    gtp1_hdr_set_npdu(hdr, 0);
    gtp1_hdr_set_next_ext_type(hdr, 0);

    EXPECT_TRUE(memcmp(buff, gtp1_u1, 8) == 0);
}

/*
 * User Datagram Protocol, Src Port: 2152, Dst Port: 2152
 * GPRS Tunneling Protocol
 *     Flags: 0x34
 *         001. .... = Version: GTP release 99 version (1)
 *         ...1 .... = Protocol type: GTP (1)
 *         .... 0... = Reserved: 0
 *         .... .1.. = Is Next Extension Header present?: Yes
 *         .... ..0. = Is Sequence Number present?: No
 *         .... ...0 = Is N-PDU number present?: No
 *     Message Type: T-PDU (0xff)
 *     Length: 445
 *     TEID: 0x01447453 (21263443)
 *     Next extension header type: PDU Session container (0x85)
 *     Extension header (PDU Session container)
 *         Extension Header Length: 1
 *         PDU Session Container
 *             0001 .... = PDU Type: UL PDU SESSION INFORMATION (1)
 *             .... 0000 = Spare: 0x0
 *             00.. .... = Spare: 0x0
 *             ..00 0001 = QoS Flow Identifier (QFI): 1
 *         Next extension header type: No more extension headers (0x00)
 */

unsigned char gtp1_u2[] = {
    0x34, 0xff, 0x01, 0xbd, 0x01, 0x44, 0x74, 0x53, 0x00, 0x00, 0x00, 0x85, 0x01, 0x10, 0x01, 0x00};

TEST(GTP1_UTILS, U2_GET)
{
    const struct gtp1_hdr *hdr = (struct gtp1_hdr *)gtp1_u2;

    // GTP Fixed Header Length + GTPv1-U Optional Headers + GTPv1-U Extension Headers
    EXPECT_TRUE(calc_gtp1_hdr_len((const char *)gtp1_u2, sizeof(gtp1_u2)) == 16);
    EXPECT_TRUE(gtp1_hdr_get_flags(hdr) == 0x34);
    EXPECT_TRUE(gtp1_hdr_get_version(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_proto(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_reserved(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_ext_flag(hdr) == 1);
    EXPECT_TRUE(gtp1_hdr_get_seq_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_npdu_flag(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_msg_type(hdr) == 0xff);
    EXPECT_TRUE(gtp1_hdr_get_msg_len(hdr) == 445);
    EXPECT_TRUE(gtp1_hdr_get_teid(hdr) == 21263443);
    EXPECT_TRUE(gtp1_hdr_get_seq(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_npdu(hdr) == 0);
    EXPECT_TRUE(gtp1_hdr_get_next_ext_type(hdr) == 0x85);

    char buff[1024] = {0};
    gtp1_hdr_to_str(hdr, buff, sizeof(buff));
    printf("%s\n", buff);
}

TEST(GTP1_UTILS, U2_SET)
{
    char buff[16] = {0};

    struct gtp1_hdr *hdr = (struct gtp1_hdr *)buff;
    gtp1_hdr_set_flags(hdr, 0x34);
    gtp1_hdr_set_version(hdr, 1);
    gtp1_hdr_set_proto(hdr, 1);
    gtp1_hdr_set_reserved(hdr, 0);
    gtp1_hdr_set_ext_flag(hdr, 1);
    gtp1_hdr_set_seq_flag(hdr, 0);
    gtp1_hdr_set_npdu_flag(hdr, 0);
    gtp1_hdr_set_msg_type(hdr, 0xff);
    gtp1_hdr_set_msg_len(hdr, 445);
    gtp1_hdr_set_teid(hdr, 21263443);
    gtp1_hdr_set_seq(hdr, 0);
    gtp1_hdr_set_npdu(hdr, 0);
    gtp1_hdr_set_next_ext_type(hdr, 0x85);

    // not compare the extension header
    EXPECT_TRUE(memcmp(buff, gtp1_u2, 12) == 0);
}

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