//===========================================================================
//  @(#) $Name: cflowd-2-1-b1 $
//  @(#) $Id: cfdnets.cc,v 1.12 1999/05/25 10:26:59 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 <string>

#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: cfdnets.cc,v 1.12 1999/05/25 10:26:59 dwm Exp $";
static CflowdVersion  g_cflowdVersion = CflowdVersion(rcsid);
CflowdConfig          g_cflowdConfig;
static bool           g_srcNetSpecified = false;
static bool           g_dstNetSpecified = false;
static ipv4addr_t     g_srcNet = 0;
static uint8_t        g_srcMaskLen = 0;
static ipv4addr_t     g_dstNet = 0;
static uint8_t        g_dstMaskLen = 0;

//----------------------------------------------------------------------------
//           static bool                               
//           NetEntryMatches(const CflowdNetMatrixKey & netmKey) 
//............................................................................
//  
//----------------------------------------------------------------------------
static bool
NetEntryMatches(const CflowdNetMatrixKey & netmKey)
{
  ipv4addr_t  netmask, netaddr;
  struct in_addr  addrIn;

  netmask = htonl((0xffffffff) << (32 - g_srcMaskLen));

  if (g_srcNetSpecified) {
    if ((netmKey.Src() & netmask) != g_srcNet) {
      return(false);
    }

    if (g_srcMaskLen > netmKey.SrcMaskLen()) {
      return(false);
    }
  }

  netmask = htonl((0xffffffff) << (32 - g_dstMaskLen));

  if (g_dstNetSpecified) {
    if ((netmKey.Dst() & netmask) != g_dstNet) {
      return(false);
    }

    if (g_dstMaskLen > netmKey.DstMaskLen()) {
      return(false);
    }
  }
  return(true);
}


//-------------------------------------------------------------------------
//                      void Usage(const char *argv0)                      
//.........................................................................
//  
//-------------------------------------------------------------------------
void Usage(const char *argv0)
{
  cerr << "usage: " << argv0
       << " [-v] [-s srcNet/masklen] [-d dstNet/masklen]"
       << " [-t] [-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;
  bool                 printRates = true;
  char                *configFile = NULL;
  ipv4addr_t           routerIpAddr;
  CflowdTableRequest   tableRequest;
  CflowdCisco          cflowdCisco;
  struct in_addr       srcNetAddr, dstNetAddr;
  uint16_t             ifIndexMatch = (uint16_t)(-1);
  CflowdCiscoFlowInterfaceMap::const_iterator  intfIter;
  char                *ipAddrString;
  char                *maskLenString;

  if (argc < 2) {
    Usage(argv[0]);
    exit(1);
  }
  
  while ((optChar = getopt(argc,argv,"c:d:s:tv")) != EOF) {
    switch (optChar) {
      case 'c':
        configFile = (char *)strdup(optarg);
        break;
      case 'd':
        g_dstNetSpecified = true;
        ipAddrString = strtok(optarg,"/");
        if (! ipAddrString) {
          Usage(argv[0]);
          exit(1);
        }
        g_dstNet = inet_addr(ipAddrString);
        if (g_dstNet == INADDR_NONE) {
          cerr << "'" << ipAddrString << "'"
               << " is an illegal destination network address."
               << endl;
          Usage(argv[0]);
          exit(1);
        }
        maskLenString = strtok(NULL," ");
        if (! maskLenString) {
          Usage(argv[0]);
          exit(1);
        }
        g_dstMaskLen = (uint8_t)atoi(maskLenString);
        if (g_dstMaskLen > 32 || g_dstMaskLen == 0) {
          cerr << maskLenString
               << " is an illegal destination network mask length." << endl;
          Usage(argv[0]);
          exit(1);
        }
	break;
      case 's':
        g_srcNetSpecified = true;
        ipAddrString = strtok(optarg,"/");
        if (! ipAddrString) {
          Usage(argv[0]);
          exit(1);
        }
        g_srcNet = inet_addr(ipAddrString);
        if (g_srcNet == INADDR_NONE) {
          cerr << "'" << ipAddrString << "'"
               << " is an illegal source network address."
               << endl;
          Usage(argv[0]);
          exit(1);
        }
        maskLenString = strtok(NULL," ");
        if (! maskLenString) {
          Usage(argv[0]);
          exit(1);
        }
        g_srcMaskLen = (uint8_t)atoi(maskLenString);
        if (g_srcMaskLen > 32 || g_srcMaskLen == 0) {
          cerr << maskLenString
               << " is an illegal source network mask length." << endl;
          Usage(argv[0]);
          exit(1);
        }
        break;
      case 't':
        printRates = false;
        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_cflowdNetMatrixMask);

  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("cfdnets",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++) {
    CflowdNetMatrix::const_iterator  netIter;

    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(18) << "source network"
         << "  " << setw(18) << "dest network";
    if (printRates) {
      cout << "  " << setw(15) << "pkts/sec"
           << "  " << setw(15) << "bits/sec" << endl;
    }
    else {
      cout << "  " << setw(15) << "packets"
           << "  " << setw(15) << "bytes" << endl;
    }
    cout << "  " << "---------------" << "-" << "--" 
         << "  " << "---------------" << "-" << "--"
         << "  " << "---------------"
         << "  " << "---------------"
         << endl;
      
    for (netIter = (*intfIter).second.NetMatrix().begin();
         netIter != (*intfIter).second.NetMatrix().end(); netIter++) {
      if (NetEntryMatches((*netIter).first)) {
        srcNetAddr.s_addr = (*netIter).first.Src();
        cout << resetiosflags(ios::left) << "  " << setw(15)
             << inet_ntoa(srcNetAddr) << "/"
             << setiosflags(ios::left) << setw(2)
             << (uint16_t)(*netIter).first.SrcMaskLen()
             << setiosflags(ios::right)
             << "  ";
        dstNetAddr.s_addr = (*netIter).first.Dst();
        cout << resetiosflags(ios::left) << setw(15)
             << inet_ntoa(dstNetAddr) << "/"
             << setiosflags(ios::left) << setw(2)
             << (uint16_t)(*netIter).first.DstMaskLen()
             << resetiosflags(ios::left)
             << "  ";
        if (printRates) {
          cout << setw(15)
               << (uint64_t)((double)(*netIter).second.Pkts()/timeDelta)
               << "  " << setw(15)
               << (uint64_t)(((double)(*netIter).second.Bytes()*8.0)/timeDelta)
               << endl;
        }
        else {
          cout << setw(15) << (*netIter).second.Pkts() << "  "
               << setw(15) << (*netIter).second.Bytes() << endl;
        }
      }  // if (NetEntryMatches((*netIter).first)) ...
    }  // for (netIter = (*intfIter).second.NetMatrix().begin(); ...
    
    cout << endl;
  }
}

