%description:
Test serializing frames

%inifile: omnetpp.ini
[General]
#debugger-attach-on-startup=true
#debugger-attach-on-error=true

%includes:
#include <stdio.h>
#include <iostream>
#include "inet/common/serializer/SerializerBase.h"
#include "inet/common/serializer/headerserializers/EthernetCRC.h"
#include "inet/common/serializer/headerserializers/ethernet/EthernetSerializer.h"
#include "inet/common/serializer/headerserializers/ieee80211/Ieee80211Serializer.h"

%global:

using namespace inet;

using namespace inet::serializer;

#define PCAP_MAGIC      0xa1b2c3d4

/* "libpcap" file header (minus magic number). */
struct pcap_hdr
{
    uint32_t magic;    /* magic */
    uint16_t version_major;    /* major version number */
    uint16_t version_minor;    /* minor version number */
    uint32_t thiszone;    /* GMT to local correction */
    uint32_t sigfigs;    /* accuracy of timestamps */
    uint32_t snaplen;    /* max length of captured packets, in octets */
    uint32_t network;    /* data link type */
};

/* "libpcap" record header. */
struct pcaprec_hdr
{
    int32_t ts_sec;    /* timestamp seconds */
    uint32_t ts_usec;    /* timestamp microseconds */
    uint32_t incl_len;    /* number of octets of packet saved in file */
    uint32_t orig_len;    /* actual length of packet */
};


uint16_t swapByteOrder16(uint16_t v)
{
    return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
}

uint32_t swapByteOrder32(uint32_t v)
{
    return ((v & 0xFFL) << 24) | ((v & 0xFF00L) << 8) | ((v & 0xFF0000L) >> 8) | ((v & 0xFF000000L) >> 24);
}

#if OMNETPP_VERSION < 0x0500
//cClassDescriptor compatibility
#define getFieldValueAsString(a, b, c)         getFieldAsString((a), (b), (c))
#define getFieldProperty(a, b)                 getFieldProperty(nullptr, (a), (b))
#define getFieldName(a)                        getFieldName(nullptr, (a))
#define getFieldIsCObject(a)                   getFieldIsCObject(nullptr, (a))
#define getFieldCount()                        getFieldCount(nullptr)
#define getFieldTypeString(a)                  getFieldTypeString(nullptr, (a))
#define getFieldIsArray(a)                     getFieldIsArray(nullptr, (a))
#define getFieldArraySize(a, b)                getArraySize((a), (b))
#define getFieldStructValuePointer(a, b, c)    getFieldStructPointer((a), (b), (c))
#define getFieldTypeFlags(a)                   getFieldTypeFlags(nullptr, (a))
#define getFieldStructName(a)                  getFieldStructName(nullptr, (a))
#endif    // OMNETPP_VERSION < 0x0500

static void printobject(cObject* object)
{
    EV << object->getClassName() << ":" << endl;
    cClassDescriptor *descriptor = object->getDescriptor();
    for (int i = 0; i < descriptor->getFieldCount(); i++) {
        EV << "    " << descriptor->getFieldName(i) << ": ";
        if (descriptor->getFieldIsArray(i)) {
            for (int j = 0; j < descriptor->getFieldArraySize(object, i); j++) {
                EV << "      [" << j << "]: " << descriptor->getFieldValueAsString(object,i,j) << endl;
            }
        }
        else
            EV << descriptor->getFieldValueAsString(object,i,0) << endl;
    }
}

static void testRecord(const char* filename, bool fcs = true)
{
    FILE *pcapFile;
    struct pcap_hdr fileHeader;
    struct pcaprec_hdr recordHeader;
    static uint8_t readBuf[60000];
    static uint8_t writeBuf[60000];

    memset((void*)&readBuf, 0, sizeof(readBuf));
    memset((void*)&writeBuf, 0, sizeof(writeBuf));

    EV << "=== Testing file " << filename << endl;

    pcapFile  = fopen(filename, "rb");
    if (!pcapFile)
        throw cRuntimeError("pcap file '%s' can not opened.", filename);

    size_t err = fread(&fileHeader, sizeof(fileHeader), 1, pcapFile);
    if (err != 1)
        throw cRuntimeError("Can not read pcap fileheader from file '%s', errno is %ld.", filename, err);

    if (fileHeader.magic == 0xa1b2c3d4)
    {
    }
    else if (fileHeader.magic == 0xd4c3b2a1)
    {
        fileHeader.version_major = swapByteOrder16(fileHeader.version_major);
        fileHeader.version_minor = swapByteOrder16(fileHeader.version_minor);
        fileHeader.thiszone = swapByteOrder32(fileHeader.thiszone);
        fileHeader.sigfigs = swapByteOrder32(fileHeader.sigfigs);
        fileHeader.snaplen = swapByteOrder32(fileHeader.snaplen);
        fileHeader.network = swapByteOrder32(fileHeader.network);
    }
    else
        throw cRuntimeError("unknown pcap fileheader from file '%s'", filename);

    err = fread(&recordHeader, sizeof(recordHeader), 1, pcapFile);
    if (err != 1)
        throw cRuntimeError("Can not read pcap recordheader from file '%s', errno is %ld.", filename, err);
    if (fileHeader.magic != 0xa1b2c3d4)
    {
        recordHeader.ts_sec = swapByteOrder32(recordHeader.ts_sec);
        recordHeader.ts_usec = swapByteOrder32(recordHeader.ts_usec);
        recordHeader.orig_len = swapByteOrder32(recordHeader.orig_len);
        recordHeader.incl_len = swapByteOrder32(recordHeader.incl_len);
    }
    fread(&readBuf, recordHeader.incl_len, 1, pcapFile);
    fclose(pcapFile);

    uint32_t serializedLength = 0;

    uint32_t incl_len = recordHeader.incl_len;

    if (! fcs && fileHeader.network == LINKTYPE_ETHERNET && recordHeader.incl_len == recordHeader.orig_len) {
        // add FCS
        Buffer b(readBuf, incl_len + 4);
        b.seek(incl_len);
        uint32_t fcs = ethernetCRC(b._getBuf(), b.getPos());
        b.writeUint32(fcs);
        incl_len = b.getPos();
    }

    Buffer rb(readBuf, incl_len);
    Buffer wb(writeBuf, sizeof(writeBuf));
    Context c;
    cPacket *frame = SerializerBase::lookupAndDeserialize(rb, c, LINKTYPE, fileHeader.network);
    ASSERT(frame);
    for (cPacket *temp = frame; temp; temp = temp->getEncapsulatedPacket())
        printobject(temp);
    SerializerBase::lookupAndSerialize(frame, wb, c, LINKTYPE, fileHeader.network);
    serializedLength = wb.getPos();
    delete frame;

    if(incl_len == serializedLength)
        EV << "Frame lengths are the same" << endl;
    else
        EV << "Frame lengths are not the same: " << incl_len << " ws " << serializedLength << endl;
    if(incl_len == serializedLength && !memcmp(readBuf, writeBuf, fcs ? serializedLength : serializedLength - 4))
        EV << "Frames are the same" << endl;
    else
        EV << "Frames are not the same" << endl;
}

%activity:
testRecord("../../pcap/rarp_request.pcap", false);
testRecord("../../pcap/rarp_req_reply.pcap", false);
testRecord("../../pcap/Ethernet_Pause_Frame.pcap", false);
testRecord("../../pcap/eth_fcs_good.pcap", true);
//testRecord("../../pcap/v6.pcap", false);
//testRecord("../../pcap/tcp_ok_capture.pcap", false);
//testRecord("../../pcap/80211ack.pcap", false);

%contains-regex: stdout
=== Testing file ../../pcap/rarp_request.pcap
.*
Frames are the same
=== Testing file ../../pcap/rarp_req_reply.pcap
.*
Frames are the same
=== Testing file ../../pcap/Ethernet_Pause_Frame.pcap
.*
Frames are the same
=== Testing file ../../pcap/eth_fcs_good.pcap
.*
Frames are the same

%not-contains: stdout
Frames are not the same
