// $Id: econ_trc.c,v 1.9 2014/05/21 20:05:57 lpoulsen Exp $
//
// Copyright (c) 2011 by Afar Communications Inc, Santa Barbara, CA 93117 USA
//
// $Log: econ_trc.c,v $
// Revision 1.9  2014/05/21 20:05:57  lpoulsen
// uint32 is not long anymore, so remove long qualifiers in printf formats
//
// Revision 1.8  2011/08/29 22:29:27  lpoulsen
// Clean compilation on FC3 linux.
//
// Revision 1.7  2011/07/08 21:50:30  lpoulsen
// Eliminate (almost all) use of debug_level, use debug_flags instead.
//
// Revision 1.6  2011/06/30 22:21:02  lpoulsen
// Include unistd.h only on Linux
//
// Revision 1.5  2011/05/19 00:52:59  lpoulsen
// Clean out more warnings
//
// Revision 1.4  2011/05/18 18:51:23  lpoulsen
// Runs on Fedora 14
//
// Revision 1.3  2011/05/09 20:28:28  lpoulsen
// ES_READ_EOF is a successful comand file session - delete the trace.
//
// Revision 1.2  2011/05/09 19:44:01  lpoulsen
// Strip padding from short packets when saving to trace file
//
// Revision 1.1  2011/05/07 01:09:59  lpoulsen
// For ECON 04.07 we can now save session PCAP trace files
//
//
#ifdef LINUX
#include <unistd.h>
#endif
#include <stdio.h>
#include "afartype.h"
#include "util.h"
#ifdef WIN32
#include <winsock2.h>
#endif
#include <time.h>
#ifndef WIN32
#include <sys/time.h>
#endif
#include "adapter.h"    // to define my_mac_address
#include "pcapafar.h"
#include "epacket.h"
#include "ucon.h"
#include "afarutil.h"

#define MAXPATH 80

extern void   afar_pfc_setup(void);
extern uint32 afar_pfc_read(void);
extern uint32 afar_usec_delta(uint32 pfc_start, uint32 pfc_end);
#define ntohs(a) swap_uint16(a)
char *format_pdu(uint16 pdu); // see below

extern FILE *session_pcap_file;

char    session_pcap_filename[MAXPATH];
FILE   *session_pcap_file = NULL;
char    session_text_filename[MAXPATH];
FILE   *session_text_file = NULL;

uint32  session_pkt_no;

time_t econ_trace_start_seconds;
uint32 econ_trace_start_pfc;
uint32 econ_trace_pfc_now;

void
econ_trace_open(char *devname)
{
    struct pcap_file_header pfh;

    if (debug_flags & DBFLG_SESSION_PCAP) {
        sprintf(session_pcap_filename, "session_%s", devname);
        session_pcap_file = afar_fopen_ext(session_pcap_filename, "wb", "pcap");
        if ((debug_flags & DBFLG_SESSION_PCAP)
         && (!session_pcap_file)) {
            printf("Opening PCAP file %s.pcap ... failed",
                   session_pcap_filename);
        }
    }

    if (session_pcap_file) {
        pfh.magic = 0xa1b2c3d4;
        pfh.version_major = 2;
        pfh.version_minor = 4;
        pfh.thiszone = 0;
        pfh.sigfigs = 0;
        pfh.snaplen = 0x0000ffff;
        pfh.linktype = 1;

        fwrite(&pfh, sizeof pfh, 1, session_pcap_file);
    }

    if (debug_flags & DBFLG_SESSION_TEXT) {
        sprintf(session_text_filename, "session_%s.txt ", devname);
        session_text_file = afar_fopen_ext(session_text_filename, "w", "txt");
        if ((debug_flags & DBFLG_SESSION_TEXT)
         && (!session_text_file)) {
            printf("Opening session log file %s.txt ... failed",
                   session_text_filename);
        }
    }

    if (session_text_file) {
        fprintf(session_text_file, "ECON session to %s opened %s at %s\n\n",
                devname, afar_date(0), afar_time(0));
    }
    afar_pfc_setup();
    time(&econ_trace_start_seconds);
    econ_trace_start_pfc = afar_pfc_read();

    session_pkt_no = 0;
    return;
}

void
econ_trace_packet(econ_packet *pkt, size_t nbytes)
{
    struct   pcap_pkthdr pph;
    uint32   delta_seconds;
    uint32   delta_usecs;
    int      res;
    uint16   proto;
    boolean  from_me;

    econ_trace_pfc_now = afar_pfc_read();
    delta_usecs = afar_usec_delta(econ_trace_start_pfc, econ_trace_pfc_now);
    delta_seconds = delta_usecs / 1000000;

    proto = ntohs(pkt->econ_ehdr.epkt_proto);
    if (proto > 1500) { // Not a SNAP packet
        printf("Proto= %04X (%d)\n", proto, proto);
        return;
    }

    if ((nbytes == 60) && (proto+sizeof(epkt_header) < 60))
        nbytes = proto + sizeof(epkt_header);

#if 0
    if ((proto + sizeof(epkt_header)) != nbytes)
    {
        printf("Packet size issue: nbytes=%d proto=%d +14=%d\n",
               nbytes, proto, proto+sizeof(epkt_header));
    }
#endif

    pph.ts.tv_sec = econ_trace_start_seconds + delta_seconds;
    pph.ts.tv_usec = delta_usecs % 1000000;
    pph.caplen = pph.len = nbytes;

    session_pkt_no++;

    from_me = (0 == memcmp(&pkt->econ_ehdr.epkt_src, &my_mac_address, 6));

    if (session_pcap_file) {
        res = fwrite(&pph, sizeof pph, 1, session_pcap_file);
        if (res != 1)
            printf("*** Error writing packet trace header\n");
        res = fwrite(pkt, nbytes, 1, session_pcap_file);
        if (res != 1)
            printf("*** Error writing packet to trace file\n");
    }
    if (session_text_file) {
        fprintf(session_text_file,
                "Pkt# %4u at %4u.%03ld - %d bytes (%d)\n",
                session_pkt_no, delta_seconds, pph.ts.tv_usec/1000,
                nbytes, proto+14);
        fprintf(session_text_file,
                "                       %4.4s %8.8s %04x %6u %6u %4d bytes\n",
                (from_me ? "--> " : " <--"),
                format_pdu(ntohs(pkt->econ_chdr.econ_pdu)),
                ntohs(pkt->econ_chdr.econ_flags),
                (unsigned int)ntohl(pkt->econ_chdr.econ_tseq),
                (unsigned int)ntohl(pkt->econ_chdr.econ_rseq),
                (int)ntohl(pkt->econ_chdr.econ_nbytes));
        fprintf(session_text_file, "\n");
    }
    return;
}

void
econ_trace_close(void)
{
    if (session_pcap_file) {
        fclose(session_pcap_file);
        if (((session_result == ES_DOWNLOAD_GOOD)
          || (session_result == ES_REMOTE_CLOSE)
          || (session_result == ES_READ_EOF))
         && ((debug_flags & DBFLG_KEEP_TRACES) == 0))
        {
            unlink(session_pcap_filename);
            printf("Session successful, PCAP file deleted\n");
        }
        session_pcap_file = NULL;
    }
    if (session_text_file) {
        fclose(session_text_file);
        if (((session_result == ES_DOWNLOAD_GOOD)
          || (session_result == ES_REMOTE_CLOSE)
          || (session_result == ES_READ_EOF))
         && ((debug_flags & DBFLG_KEEP_TRACES) == 0))
        {
            unlink(session_text_filename);
            printf("Session successful, session trace text file deleted\n");
        }
        session_text_file = NULL;
    }
}

char *pdu_names[] = {
    "CONS_CMD",
    "CONS_TXT",
    "EMPTYACK",
    "BREAK   ",
    "ID_REQ  ",
    "ID_RESP ",
    "CONN_REQ",
    "CONN_RSP",
    "CLOSE_RQ",
    "FILE_REQ",
    "FILE_RSP",
    "FILEDATA",
    "END_DATA",
    "GO_AWAY ",
    NULL
};

char *
format_pdu(uint16 pdu)
{
    static char bad_pdu_name[] = "?######?";

    if (pdu <= GO_AWAY) return pdu_names[pdu];
    sprintf(bad_pdu_name, "?%5.5d ?", pdu);
    return bad_pdu_name;
}
