//===========================================================================
//  @(#) $Name: cflowd-2-1-b1 $
//  @(#) $Id: cfdifmatrix.cc,v 1.8 1999/05/26 11:36:56 dwm Exp $
//===========================================================================
//  CAIDA Copyright Notice
//
//  By accessing this software, cflowd++, you are duly informed
//  of and agree to be bound by the conditions described below in this
//  notice:
//
//  This software product, cflowd++, is developed by Daniel W. McRobb, and
//  copyrighted(C) 1998 by the University of California, San Diego
//  (UCSD), with all rights reserved.  UCSD administers the CAIDA grant,
//  NCR-9711092, under which part of this code was developed.
//
//  There is no charge for cflowd++ software. You can redistribute it
//  and/or modify it under the terms of the GNU General Public License,
//  v.  2 dated June 1991 which is incorporated by reference herein.
//  cflowd++ is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF
//  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use
//  of it will not infringe on any third party's intellectual property
//  rights.
//
//  You should have received a copy of the GNU GPL along with cflowd++.
//  Copies can also be obtained from:
//
//    http://www.gnu.org/copyleft/gpl.html
//
//  or by writing to:
//
//    University of California, San Diego
//
//    SDSC/CAIDA
//    9500 Gilman Dr., MS-0505
//    La Jolla, CA 92093 - 0505  USA
//
//  Or contact:
//
//    info@caida.org
//===========================================================================

extern "C" {
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <time.h>
}

#include <iostream.h>
#include <iomanip.h>

#include "CflowdConfig.hh"
#include "CflowdConfigLex.hh"
#include "CflowdTableRequest.hh"
#include "CflowdVersion.hh"

#ifndef INADDR_NONE
  #define INADDR_NONE 0xffffffff
#endif

extern char *tzname[2];

static const string         rcsid = "@(#) $Name: cflowd-2-1-b1 $ $Id: cfdifmatrix.cc,v 1.8 1999/05/26 11:36:56 dwm Exp $";
static const CflowdVersion  g_cflowdVersion = CflowdVersion(rcsid);
static CflowdConfig         g_cflowdConfig;

//-------------------------------------------------------------------------
//                      void Usage(const char *argv0)                      
//.........................................................................
//  
//-------------------------------------------------------------------------
void Usage(const char *argv0)
{
  cerr << "usage: " << argv0 << " [-v] [-c configfile] routerIpAddr [ifIndex]"
       << endl;
  return;
}

//-------------------------------------------------------------------------
//                     int main(int argc, char *argv[])                    
//.........................................................................
//  
//-------------------------------------------------------------------------
int main(int argc, char *argv[])
{
  struct sockaddr_un   servSockAddr;
  int                  servSockAddrLen;
  int                  sockFd;
  extern int           optind;
  extern char         *optarg;
  int                  optChar;
  char                *configFile = NULL;
  ipv4addr_t           routerIpAddr;
  CflowdTableRequest   tableRequest;
  CflowdCisco          cflowdCisco;
  uint16_t             ifIndexMatch = (uint16_t)(-1);
  CflowdCiscoFlowInterfaceMap::const_iterator  intfIter;
  
  if (argc < 2) {
    Usage(argv[0]);
    exit(1);
  }
  
  while ((optChar = getopt(argc,argv,"c:v")) != EOF) {
    switch (optChar) {
      case 'c':
        configFile = (char *)strdup(optarg);
        break;
      case 'v':
        cerr << g_cflowdVersion.Name() << "  " << g_cflowdVersion.Id() << endl;
        exit(0);
        break;
      case '?':
      default:
        Usage(argv[0]);
        exit(1);
        break;
    }
  }

  if (optind > (argc - 1)) {
    Usage(argv[0]);
    exit(1);
  }
  
  tableRequest.RouterIpAddr(inet_addr(argv[optind]));
  tableRequest.TableIndex(CflowdCisco::k_cflowdInterfaceMatrixMask);

  if ((argc - 1) > optind) {
    optind++;
    ifIndexMatch = atoi(argv[optind]);
  }
  
  if (!configFile) {
    configFile =
      (char *)strdup(CflowdConfig::k_defaultCflowdConfigFile.c_str());
  }

  if (LoadConfigFile(configFile,g_cflowdConfig) < 0) {
    cerr << "failed to load config file '" << configFile << "': "
         << strerror(errno) << " {" << __FILE__ << ":" << __LINE__ << "}"
         << endl;
    Usage(argv[0]);
    exit(1);
  }
  
  openlog("cfdifmatrix",LOG_PID,g_cflowdConfig.LogFacility());
  
  sockFd = socket(AF_UNIX,SOCK_STREAM,0);
  if (sockFd < 0) {
    cerr << "socket(AF_UNIX,SOCK_STREAM,0) failed: " << strerror(errno)
         << " {" << __FILE__ << ":" << __LINE__ << "}" << endl;
    exit(1);
  }
  memset(&servSockAddr,0,sizeof(servSockAddr));
  servSockAddr.sun_family = AF_UNIX;
  strcpy(servSockAddr.sun_path,g_cflowdConfig.TableSockFile().c_str());
  servSockAddrLen = strlen(servSockAddr.sun_path) + 1 +
    sizeof(servSockAddr.sun_family);
  if (connect(sockFd,(struct sockaddr *)&servSockAddr,servSockAddrLen) < 0) {
    cerr << "connect(" << sockFd << "," << (void *)&servSockAddr
         << "(\"" << servSockAddr.sun_path << "\",AF_UNIX)" << ","
         << servSockAddrLen << ") failed: " << strerror(errno)
         << " {" << __FILE__ << ":" << __LINE__ << "}" << endl;
    exit(1);
  }

  if (tableRequest.Write(sockFd) < 0) {
    cerr << "tableRequest.Write(" << sockFd << ") failed! {"
         << __FILE__ << ":" << __LINE__ << "}" << endl;
    exit(1);
  }
  
  if (cflowdCisco.read(sockFd) < 0) {
    cerr << "cflowdCisco.read(" << sockFd << ") failed! {" << __FILE__
         << ":" << __LINE__ << "}" << endl;
    // exit(1);
  }

  time_t  startTime, endTime, timeDelta;

  startTime = cflowdCisco.LastCleared();
  endTime = cflowdCisco.LastUpdated();
  timeDelta = endTime - startTime;
  
  struct tm  *localTm;

  localTm = localtime(&startTime);
  
  cout << "period: " << setfill('0')
       << setw(2) << (int)localTm->tm_mon+1 << "/"
       << setw(2) << localTm->tm_mday << "/"
       << setw(4) << localTm->tm_year + 1900 << " "
       << setw(2) << localTm->tm_hour << ":"
       << setw(2) << localTm->tm_min << ":"
       << setw(2) << localTm->tm_sec << " - ";
  localTm = localtime(&endTime);
  cout << setw(2) << (int)localTm->tm_mon+1 << "/"
       << setw(2) << localTm->tm_mday << "/"
       << setw(4) << localTm->tm_year + 1900 << " "
       << setw(2) << localTm->tm_hour << ":"
       << setw(2) << localTm->tm_min << ":"
       << setw(2) << localTm->tm_sec << " " << tzname[localTm->tm_isdst]
       << " (" << timeDelta/60 << " min, " << timeDelta%60 << " sec)"
       << endl << endl << setfill(' ');
  
  for (intfIter = cflowdCisco.Interfaces().begin();
       intfIter != cflowdCisco.Interfaces().end(); intfIter++) {
    CflowdInterfaceMatrix::const_iterator  intfmIter;

    if (ifIndexMatch != (uint16_t)(-1)) {
      if ((*intfIter).first != ifIndexMatch) {
        continue;
      }
    }
    
    cout << "ifIndex: " << (*intfIter).first;
    if ((*intfIter).second.IpAddr() != INADDR_NONE) {
      cout << " (" << (*intfIter).second.IfDescr();
      struct in_addr inAddr;
      inAddr.s_addr = (*intfIter).second.IpAddr();
      cout << " " << inet_ntoa(inAddr) << ")";
    }
    cout << endl;
    
    
    cout << "  " << setw(7) << "srcIntf"
         << "  " << setw(7) << "dstIntf"
         << "  " << setw(13) << "packets"
         << "  " << setw(13) << "bytes"
         << "  " << setw(13) << "pkts/sec"
         << "  " << setw(13) << "bits/sec" << endl;
    cout << "  " << "-------"
         << "  " << "-------"
         << "  " << "-------------"
         << "  " << "-------------"
         << "  " << "-------------"
         << "  " << "-------------"
         << endl;
      
    for (intfmIter = (*intfIter).second.InterfaceMatrix().begin();
         intfmIter != (*intfIter).second.InterfaceMatrix().end();
         intfmIter++) {
      cout << "  " << setw(7) << (*intfmIter).first.Src()
           << "  " << setw(7) << (*intfmIter).first.Dst()
           << "  " << setw(13) << (*intfmIter).second.Pkts()
           << "  " << setw(13) << (*intfmIter).second.Bytes()
           << "  " << setw(13)
           << (uint64_t)((double)(*intfmIter).second.Pkts()/timeDelta)
           << "  " << setw(13)
           << (uint64_t)(((double)(*intfmIter).second.Bytes()*8.0)/timeDelta)
           << endl;
    }
    cout << endl;
  }
}

