//===========================================================================
//  @(#) $Name: cflowd-2-1-b1 $
//  @(#) $Id: CflowdCisco.cc,v 1.31 2000/08/03 17:20:45 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 <time.h>
  
#include "caida_t.h"
}

#include <string>
#include <strstream.h>

#include "CflowdCisco.hh"

static const string rcsid = "@(#) $Name: cflowd-2-1-b1 $ $Id: CflowdCisco.cc,v 1.31 2000/08/03 17:20:45 dwm Exp $";

static Oid   g_ipAdEntIfIndexOid("1.3.6.1.2.1.4.20.1.2");
static Oid   g_ifDescrOid("1.3.6.1.2.1.2.2.1.2");

//-------------------------------------------------------------------------
//                        CflowdCisco::CflowdCisco()                       
//.........................................................................
//  
//-------------------------------------------------------------------------
CflowdCisco::CflowdCisco()
{
  this->_ipAddress     = 0;
  this->_flowPort      = 0;
  this->_localAS       = 0;
  this->_snmpCommunity = string("public");
  this->_lastCleared   = time((time_t *)NULL);
  this->_lastUpdated   = 0;
  this->_tableIndex    = 0;
  this->_flowLogger    = (CflowdRawFlowLogger *)NULL;
  this->_missedFlowsThreshold = 0;
  this->_haveInterfaceInfo = false;
  this->_lastSnmpQueryTime = 0;
}

//----------------------------------------------------------------------------
//        CflowdCisco::CflowdCisco(const CflowdCisco & cflowdCisco) 
//............................................................................
//  
//----------------------------------------------------------------------------
CflowdCisco::CflowdCisco(const CflowdCisco & cflowdCisco)
{
  this->_ipAddress     = cflowdCisco.IpAddress();
  this->_flowPort      = cflowdCisco.FlowPort();
  this->_localAS       = cflowdCisco.LocalAS();
  this->_snmpCommunity = cflowdCisco.SnmpCommunity();
  this->_lastCleared   = cflowdCisco.LastCleared();
  this->_lastUpdated   = cflowdCisco.LastUpdated();
  this->_tableIndex    = cflowdCisco.TableIndex();
  this->CreateFlowLogger(cflowdCisco.FlowLogger()->FlowDirectory(),
                         cflowdCisco.FlowLogger()->NumLogs(),
                         cflowdCisco.FlowLogger()->LogSize());
  this->_missedFlowsThreshold = cflowdCisco.MissedFlowsThreshold();
  this->_haveInterfaceInfo = cflowdCisco._haveInterfaceInfo;
  this->_lastSnmpQueryTime = cflowdCisco._lastSnmpQueryTime;
  this->_flowEngines = cflowdCisco.FlowEngines();
  this->_interfaces = cflowdCisco.Interfaces();
  
}

//-------------------------------------------------------------------------
//                       CflowdCisco::~CflowdCisco()                       
//.........................................................................
//  
//-------------------------------------------------------------------------
CflowdCisco::~CflowdCisco()
{
  if (this->_flowLogger != (CflowdRawFlowLogger *)NULL) {
    delete(this->_flowLogger);
    this->_flowLogger = (CflowdRawFlowLogger *)NULL;
  }
  if (this->FlowEngines().size() > 0)
    this->FlowEngines().erase(this->FlowEngines().begin(),
                              this->FlowEngines().end());
  if (this->Interfaces().size() > 0)
    this->Interfaces().erase(this->Interfaces().begin(),
                             this->Interfaces().end());
}

//-------------------------------------------------------------------------
//                ipv4addr_t CflowdCisco::IpAddress() const                
//.........................................................................
//  
//-------------------------------------------------------------------------
ipv4addr_t CflowdCisco::IpAddress() const
{
  return(this->_ipAddress);
}

//-------------------------------------------------------------------------
//           ipv4addr_t CflowdCisco::IpAddress(ipv4addr_t ipAddr)          
//.........................................................................
//  
//-------------------------------------------------------------------------
ipv4addr_t CflowdCisco::IpAddress(ipv4addr_t ipAddr)
{
  this->_ipAddress = ipAddr;
  return(this->_ipAddress);
}

//-------------------------------------------------------------------------
//                  uint16_t CflowdCisco::FlowPort() const                 
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t CflowdCisco::FlowPort() const
{
  return(this->_flowPort);
}

//-------------------------------------------------------------------------
//            uint16_t CflowdCisco::FlowPort(uint16_t flowPort)            
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t CflowdCisco::FlowPort(uint16_t flowPort)
{
  this->_flowPort = flowPort;
  return(this->_flowPort);
}

//----------------------------------------------------------------------------
//            const string & CflowdCisco::SnmpCommunity() const 
//............................................................................
//  
//----------------------------------------------------------------------------
const string & CflowdCisco::SnmpCommunity() const
{
  return(this->_snmpCommunity);
}

//----------------------------------------------------------------------------
// const string & CflowdCisco::SnmpCommunity(const string & snmpCommunity) 
//............................................................................
//  
//----------------------------------------------------------------------------
const string & CflowdCisco::SnmpCommunity(const string & snmpCommunity)
{
  this->_snmpCommunity = snmpCommunity;
  return(this->_snmpCommunity);
}

//-------------------------------------------------------------------------
//                  uint16_t CflowdCisco::LocalAS() const                  
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t CflowdCisco::LocalAS() const
{
  return(this->_localAS);
}

//-------------------------------------------------------------------------
//             uint16_t CflowdCisco::LocalAS(uint16_t localAS)             
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t CflowdCisco::LocalAS(uint16_t localAS)
{
  this->_localAS = localAS;
  return(this->_localAS);
}

//-------------------------------------------------------------------------
//                 uint16_t CflowdCisco::TableIndex() const                 
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t CflowdCisco::TableIndex() const
{
  return(this->_tableIndex);
}

//-------------------------------------------------------------------------
//           uint16_t CflowdCisco::TableIndex(uint16_t tableIndex)           
//.........................................................................
//  
//-------------------------------------------------------------------------
uint16_t CflowdCisco::TableIndex(uint16_t tableIndex)
{
  this->_tableIndex = tableIndex;
  return(this->_tableIndex);
}

//-------------------------------------------------------------------------
//           int CflowdCisco::AddFlow(const CflowdRawFlow & flow)          
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdCisco::AddFlow(const CflowdRawFlow & flow)
{
  uint16_t  inputIfIndex;

  if (! (flow.Index() & CflowdRawFlow::k_inputIfIndexMask)) {
    inputIfIndex = 0;
  }
  else {
    inputIfIndex = flow.InputIfIndex();
  }
  
  if (this->_tableIndex & k_cflowdProtocolTableMask) {
    this->Interfaces()[inputIfIndex].ProtocolTable().AddFlow(flow);
  }

  if (this->_tableIndex & k_cflowdPortTableMask) {
    cerr << "port table not yet implemented! {"
         << __FILE__ << ":" << __LINE__ << "}"
         << endl;
  }

  //  update the net matrix
  if (this->_tableIndex & k_cflowdNetMatrixMask) {
    this->Interfaces()[inputIfIndex].NetMatrix().AddFlow(flow);
  }

  //  update the AS matrix
  if (this->_tableIndex & k_cflowdAsMatrixMask) {
    this->Interfaces()[inputIfIndex].AsMatrix().AddFlow(flow);
  }

  if (this->_tableIndex & k_cflowdRawFlowMask) {
    this->_flowLogger->AddFlow(flow);
  }

  //  update the port matrix
  if (this->_tableIndex & k_cflowdPortMatrixMask) {
    this->Interfaces()[inputIfIndex].PortMatrix().AddFlow(flow);
  }

  //  update the interface matrix
  if (this->_tableIndex & k_cflowdInterfaceMatrixMask) {
    this->Interfaces()[inputIfIndex].InterfaceMatrix().AddFlow(flow);
  }

  //  update the nextHop table
  if (this->_tableIndex & k_cflowdNextHopTableMask) {
    this->Interfaces()[inputIfIndex].NextHopTable().AddFlow(flow);
  }

  //  update the TOS table
  if (this->_tableIndex & k_cflowdTosTableMask) {
    this->Interfaces()[inputIfIndex].TosTable().AddFlow(flow);
  }

  this->_lastUpdated = time((time_t *)NULL);
  
  return(0);
}

//-------------------------------------------------------------------------
//                istream & CflowdCisco::read(istream & is)                
//.........................................................................
//  
//-------------------------------------------------------------------------
istream & CflowdCisco::read(istream & is)
{
  cerr << "CflowdCisco::read(istream & is) not implemented! {"
       << __FILE__ << ":" << __LINE__ << "}"
       << endl;
  return(is);
}

//-------------------------------------------------------------------------
//                      int CflowdCisco::read(int fd)                      
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdCisco::read(int fd)
{
  int     rc;
  int     bytesRead = 0;

  //  read the Cisco's IP address
  rc = g_CfdArtsPrimitive.FdRead(fd,&this->_ipAddress,
                                 sizeof(this->_ipAddress));
  
  if (rc < (int)sizeof(this->_ipAddress)) {
    syslog(LOG_ERR,"[E] FdRead(%d,%p,%d) failed: %m {%s:%d}",
           fd,&this->_ipAddress,sizeof(this->_ipAddress),__FILE__,__LINE__);
    return(-1);
  }
  bytesRead += rc;
  
  //  read the start time of the data
  rc = g_CfdArtsPrimitive.ReadUint32(fd,this->_lastCleared,
                                     sizeof(this->_lastCleared));
  if (rc < (int)sizeof(this->_lastCleared)) {
    syslog(LOG_ERR,"[E] ReadUint32(%d,%p,%d) failed: %m {%s:%d}",
           fd,&this->_lastCleared,sizeof(this->_lastCleared),
           __FILE__,__LINE__);
    return(-1);
  }
  bytesRead += rc;

  //  read the end time of the data
  rc = g_CfdArtsPrimitive.ReadUint32(fd,this->_lastUpdated,
                                     sizeof(this->_lastUpdated));
  if (rc < (int)sizeof(this->_lastUpdated)) {
    syslog(LOG_ERR,"[E] ReadUint32(%d,%p,%d) failed: %m {%s:%d}",
           fd,&this->_lastUpdated,sizeof(this->_lastUpdated),
           __FILE__,__LINE__);
    return(-1);
  }
  bytesRead += rc;
  
  //  We need to read data for all interfaces.  Currently the
  //  table index is not interface-specific, but it's written
  //  per-interface anyway.  So we'll read the number of interfaces,
  //  then the tabular data for each interface.

  uint16_t   numInterfaces;

  rc = g_CfdArtsPrimitive.ReadUint16(fd,numInterfaces,sizeof(numInterfaces));
  if (rc < (int)sizeof(numInterfaces)) {
    syslog(LOG_ERR,"[E] ReadUint16(%d,%p,%d) failed: %m {%s:%d}",
           fd,&numInterfaces,sizeof(numInterfaces),__FILE__,__LINE__);
    return(-1);
  }
  bytesRead += rc;

  uint16_t                  ifNum;
  uint16_t                  ifIndex;
  
  for (ifNum = 0; ifNum < numInterfaces; ifNum++) {

    CflowdCiscoFlowInterface  ciscoInterface;

    //  read the interface number
    rc = g_CfdArtsPrimitive.ReadUint16(fd,ifIndex,sizeof(ifIndex));
    if (rc < (int)sizeof(ifIndex)) {
      syslog(LOG_ERR,"[E] ReadUint16(%d,%p,%d) failed: %m {%s:%d}",
             fd,&ifIndex,sizeof(ifIndex),__FILE__,__LINE__);
      return(-1);
    }
    bytesRead += rc;

    //  read the ifDescr
    uint8_t  ifDescrLength;
    rc = g_CfdArtsPrimitive.FdRead(fd,&ifDescrLength,1);
    if (rc < 1) {
      syslog(LOG_ERR,"[E] FdRead(%d,%p,1) failed: %m {%s:%d}",
             fd,&ifDescrLength,__FILE__,__LINE__);
      return(-1);
    }
    bytesRead += rc;
    if (ifDescrLength > 0) {
      char *buf = (char *)malloc(ifDescrLength + 1);
      if (! buf) {
        syslog(LOG_ERR,"[E] malloc(%d) failed: %m {%s:%d}",
               ifDescrLength + 1,__FILE__,__LINE__);
        return(-1);
      }
      memset(buf,0,ifDescrLength + 1);
      rc = g_CfdArtsPrimitive.FdRead(fd,buf,ifDescrLength);
      if (rc < ifDescrLength) {
        syslog(LOG_ERR,"[E] FdRead(%d,%p,%d) failed: %m {%s:%d}",
               fd,buf,ifDescrLength,__FILE__,__LINE__);
        free(buf);
        return(-1);
      }
      bytesRead += rc;
      ciscoInterface.IfDescr(buf);
      free(buf);
    }

    //  read the IP address
    ipv4addr_t  intfIpAddr;
    rc = g_CfdArtsPrimitive.ReadIpv4Network(fd,intfIpAddr,4);
    if (rc < 4) {
      syslog(LOG_ERR,"[E] ReadIpv4Network(%d,%p,4) failed: %m {%s:%d}",
             fd,&intfIpAddr,__FILE__,__LINE__);
      return(-1);
    }
    bytesRead += rc;
    ciscoInterface.IpAddr(intfIpAddr);
    
    //  read the table index

    rc = g_CfdArtsPrimitive.ReadUint16(fd,this->_tableIndex,
                                       sizeof(this->_tableIndex));
    if (rc < (int)sizeof(this->_tableIndex)) {
      syslog(LOG_ERR,"[E] ReadUint16(%d,%p,%d) failed: %m {%s:%d}",
             fd,&(this->_tableIndex),sizeof(this->_tableIndex));
      return(-1);
    }

    //  read the tabular data
    
    if (this->_tableIndex & k_cflowdProtocolTableMask) {
      if ((rc = ciscoInterface.ProtocolTable().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.ProtocolTable().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    if (this->_tableIndex & k_cflowdNetMatrixMask) {
      if ((rc = ciscoInterface.NetMatrix().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.NetMatrix().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    if (this->_tableIndex & k_cflowdAsMatrixMask) {
      if ((rc = ciscoInterface.AsMatrix().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.AsMatrix().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    if (this->_tableIndex & k_cflowdPortMatrixMask) {
      if ((rc = ciscoInterface.PortMatrix().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.PortMatrix().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    if (this->_tableIndex & k_cflowdInterfaceMatrixMask) {
      if ((rc = ciscoInterface.InterfaceMatrix().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.InterfaceMatrix().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    if (this->_tableIndex & k_cflowdNextHopTableMask) {
      if ((rc = ciscoInterface.NextHopTable().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.NextHopTable().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    if (this->_tableIndex & k_cflowdTosTableMask) {
      if ((rc = ciscoInterface.TosTable().read(fd)) < 0) {
        syslog(LOG_ERR,"[E] ciscoInterface.TosTable().read(%d)"
               " failed: %m {%s:%d}",fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesRead += rc;
    }
    this->Interfaces()[ifIndex] = ciscoInterface;
  }
  return(bytesRead);
}

//-------------------------------------------------------------------------
//             ostream & CflowdCisco::write(ostream & os) const            
//.........................................................................
//  
//-------------------------------------------------------------------------
ostream & CflowdCisco::write(ostream & os) const
{
  cerr << "CflowdCisco::write(istream & is) not implemented! {"
       << __FILE__ << ":" << __LINE__ << "}"
       << endl;
  return(os);
}

//-------------------------------------------------------------------------
//                   int CflowdCisco::write(int fd) const                  
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdCisco::write(int fd) const
{
  int     rc;
  int     bytesWritten = 0;

  //  write the Cisco's IP address
  rc = g_CfdArtsPrimitive.FdWrite(fd,&this->_ipAddress,
                                  sizeof(this->_ipAddress));
  if (rc < (int)sizeof(this->_ipAddress)) {
    syslog(LOG_ERR,"[E] FdWrite(%d,%p,%d) failed: %m {%s:%d}",
           fd,&this->_ipAddress,sizeof(this->_ipAddress),
           __FILE__,__LINE__);
    return(-1);
  }
  
  bytesWritten += rc;

  //  write the time we last cleared (start time of the data)
  rc = g_CfdArtsPrimitive.WriteUint32(fd,this->_lastCleared,
                                      sizeof(this->_lastCleared));
  
  if (rc < (int)sizeof(this->_lastCleared)) {
    syslog(LOG_ERR,"[E] WriteUint32(%d,%p,%d) failed: %m {%s:%d}",
           fd,&this->_lastCleared,sizeof(this->_lastCleared),
           __FILE__,__LINE__);
    return(-1);
  }
  bytesWritten += rc;

  //  write the time we last updated (end time of the data)
  rc = g_CfdArtsPrimitive.WriteUint32(fd,this->_lastUpdated,
                                      sizeof(this->_lastUpdated));
  if (rc < (int)sizeof(this->_lastUpdated)) {
    syslog(LOG_ERR,"[E] write(%d,%p,%d) failed: %m {%s:%d}",
           fd,&this->_lastUpdated,sizeof(this->_lastUpdated),
           __FILE__,__LINE__);
    return(-1);
  }
  bytesWritten += rc;
  
  //  We need to write data for all interfaces.  Currently our
  //  table index is not interface-specific, but we'll write it
  //  per-interface anyway.  So we'll write the number of interfaces,
  //  then the tabular data for each interface.

  uint16_t   numInterfaces;

  numInterfaces = this->Interfaces().size();

  rc = g_CfdArtsPrimitive.WriteUint16(fd,numInterfaces,sizeof(numInterfaces));
  if (rc < (int)sizeof(numInterfaces))
    return(-1);
  bytesWritten += rc;

  CflowdCiscoFlowInterfaceMap::iterator  intfIter;
  uint16_t                               ifIndex;
  
  for (intfIter = this->Interfaces().begin();
       intfIter != this->Interfaces().end(); intfIter++) {
    //  write the interface number
    ifIndex = (*intfIter).first;
    rc = g_CfdArtsPrimitive.WriteUint16(fd,ifIndex,sizeof(ifIndex));
    if (rc < (int)sizeof(ifIndex)) {
      syslog(LOG_ERR,"[E] WriteUint16(%d,%p,%d) failed: %m {%s:%d}",
             fd,&ifIndex,sizeof(ifIndex),__FILE__,__LINE__);
      return(-1);
    }
    bytesWritten += rc;

    //  write the ifDescr
    uint8_t  ifDescrLength = (*intfIter).second.IfDescr().length();
    rc = g_CfdArtsPrimitive.FdWrite(fd,&ifDescrLength,1);
    if (rc < (int)1) {
      syslog(LOG_ERR,"[E] FdWrite(%d,%p,1) failed: %m {%s:%d}",
             fd,&ifDescrLength,__FILE__,__LINE__);
      return(-1);
    }
    bytesWritten += rc;
    if (ifDescrLength > 0) {
      rc = g_CfdArtsPrimitive.FdWrite(fd,(*intfIter).second.IfDescr().c_str(),
                                      ifDescrLength);
      if (rc < ifDescrLength) {
        syslog(LOG_ERR,"[E] FdWrite(%d,%p,%d) failed: %m {%s:%d}",
               fd,(*intfIter).second.IfDescr().c_str(),ifDescrLength,
               __FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    
    //  write the IP address
    rc = g_CfdArtsPrimitive.WriteIpv4Network(fd,(*intfIter).second.IpAddr(),4);
    if (rc < 4) {
      syslog(LOG_ERR,"[E] WriteIpv4Network(%d,%x,4) failed: %m {%s:%d}",
             fd,(*intfIter).second.IpAddr(),__FILE__,__LINE__);
      return(-1);
    }
    bytesWritten += rc;
    
    //  write the table index
    rc = g_CfdArtsPrimitive.WriteUint16(fd,this->_tableIndex,
                                        sizeof(this->_tableIndex));
    if (rc < (int)sizeof(this->_tableIndex)) {
      syslog(LOG_ERR,"[E] WriteUint16(%d,%p,%d) failed: %m {%s:%d}",
             fd,&this->_tableIndex,sizeof(this->_tableIndex),
             __FILE__,__LINE__);
      return(-1);
    }
    bytesWritten += rc;

    //  write the tabular data
    
    if (this->_tableIndex & k_cflowdProtocolTableMask) {
      if ((rc = (*intfIter).second.ProtocolTable().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] ProtocolTable().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    
    if (this->_tableIndex & k_cflowdNetMatrixMask) {
      if ((rc = (*intfIter).second.NetMatrix().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] NetMatrix().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    if (this->_tableIndex & k_cflowdAsMatrixMask) {
      if ((rc = (*intfIter).second.AsMatrix().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] AsMatrix().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    if (this->_tableIndex & k_cflowdPortMatrixMask) {
      if ((rc = (*intfIter).second.PortMatrix().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] PortMatrix().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    if (this->_tableIndex & k_cflowdInterfaceMatrixMask) {
      if ((rc = (*intfIter).second.InterfaceMatrix().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] InterfaceMatrix().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    if (this->_tableIndex & k_cflowdNextHopTableMask) {
      if ((rc = (*intfIter).second.NextHopTable().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] NextHopTable().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
    if (this->_tableIndex & k_cflowdTosTableMask) {
      if ((rc = (*intfIter).second.TosTable().write(fd)) < 0) {
        syslog(LOG_ERR,"[E] TosTable().write(%d) failed {%s:%d}",
               fd,__FILE__,__LINE__);
        return(-1);
      }
      bytesWritten += rc;
    }
  }

  return(bytesWritten);
}

//-------------------------------------------------------------------------
//                  int CflowdCisco::ClearTableData()
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdCisco::ClearTableData()
{
  this->Interfaces().erase(this->Interfaces().begin(),
                           this->Interfaces().end());
  this->_lastCleared = this->_lastUpdated;

  CflowdCiscoFlowEngineMap::iterator  engineIter;

  for (engineIter = this->FlowEngines().begin();
       engineIter != this->FlowEngines().end(); engineIter++) {
    for (uint8_t aggMethod = 0; aggMethod <= k_CiscoV8FlowExportMaxAggType;
         aggMethod++) {
      if ((*engineIter).second.MissedFlows(aggMethod) >
          this->_missedFlowsThreshold) {
        struct in_addr  addrIn;
        addrIn.s_addr = this->IpAddress();
        uint64_t totalFlows = ((*engineIter).second.MissedFlows(aggMethod) +
                               (*engineIter).second.FlowsReceived(aggMethod));
        syslog(LOG_INFO,
               "[I] missed %u of %u flows from %s"
               " engine %d agg_method %d (%g%% loss)",
               (*engineIter).second.MissedFlows(aggMethod),
               totalFlows,
               inet_ntoa(addrIn),
               (*engineIter).first,
               aggMethod,
               ((*engineIter).second.MissedFlows(aggMethod) * 100.0) /
               totalFlows);
      }
      (*engineIter).second.MissedFlows(0,aggMethod);
      (*engineIter).second.FlowsReceived(0,aggMethod);
    }
  }
  
  return(0);
}

//-------------------------------------------------------------------------
//     int CflowdCisco::CreateFlowLogger(const string & flowDirectory,     
//                                       int numLogs, int logSize)         
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdCisco::CreateFlowLogger(const string & flowDirectory,
                                  int numLogs, int logSize)
{
  struct in_addr  addrIn;
  ostrstream      flowFilePrefix;
  
  addrIn.s_addr = this->_ipAddress;
  flowFilePrefix << inet_ntoa(addrIn) << ".flows" << ends;
  this->_flowLogger = new CflowdRawFlowLogger(flowDirectory,
                                              flowFilePrefix.str(),
                                              numLogs,logSize);
  flowFilePrefix.freeze(0);

  if (this->_flowLogger == (CflowdRawFlowLogger *)0) {
    syslog(LOG_ERR,"[E] unable to create raw flow logger for %s {%s:%d}",
           inet_ntoa(addrIn),__FILE__,__LINE__);
    this->_tableIndex &= (~CflowdCisco::k_cflowdRawFlowMask);
    return(-1);
  }
  
  if (this->_flowLogger->MapAddr() == (caddr_t)(-1)) {
    //  failed to create valid flow logger.  Log, delete the flow
    //  logger, and disable raw flow logging.
    syslog(LOG_ERR,"[E] unable to create raw flow logger for %s {%s:%d}",
           inet_ntoa(addrIn),__FILE__,__LINE__);
    delete(this->_flowLogger);
    this->_tableIndex &= (~CflowdCisco::k_cflowdRawFlowMask);
    syslog(LOG_WARNING,"[W] disabled raw flow collection for %s {%s:%d}",
           inet_ntoa(addrIn),__FILE__,__LINE__);
    return(-1);
  }
  
  return(0);
}

//----------------------------------------------------------------------------
//        void CflowdCisco::GetInterfaceDescriptions(Snmp & session, 
//                                                   CTarget & target) 
//............................................................................
//  
//----------------------------------------------------------------------------
void CflowdCisco::GetInterfaceDescriptions(Snmp & session,
                                           CTarget & target)
{
  Vb        varBinding;
  Pdu       pdu;
  int       status;
  uint16_t  ifIndex;
  Oid       receivedOid;
  CflowdCiscoFlowInterfaceMap::iterator  intfMapIter;
  
  varBinding.set_oid(g_ifDescrOid);
  pdu += varBinding;

  while ((status = session.get_next(pdu,target)) == SNMP_CLASS_SUCCESS) {
    for (int i = 0; i < pdu.get_vb_count(); i++) {
      pdu.get_vb(varBinding,i);
      varBinding.get_oid(receivedOid);
      if (g_ifDescrOid.nCompare(g_ifDescrOid.len(),receivedOid) == 0) {
        ifIndex = receivedOid[receivedOid.len() - 1];
        intfMapIter = this->_interfaces.find(ifIndex);
        if (intfMapIter != this->_interfaces.end()) {
          intfMapIter->second.IfDescr((const char *)(varBinding.get_printable_value()));
        }
      } else {
        pdu.delete_vb(i);
      }
    }
    if (pdu.get_vb_count() < 1)
      break;
  }
  return;
}

//----------------------------------------------------------------------------
//              ipv4addr_t IpAdEntIfIndexOidIpAddr(Oid & oid) 
//............................................................................
//  
//----------------------------------------------------------------------------
ipv4addr_t IpAdEntIfIndexOidIpAddr(Oid & oid)
{
  ipv4addr_t  ipAddr = 0;
  for (int i = oid.len() - 4; i < oid.len(); i++) {
    ipAddr |= ((ipv4addr_t)(oid[i]) << ((oid.len() - (i + 1)) * 8));
  }

  ipAddr = htonl(ipAddr);
  return(ipAddr);
}
  
//----------------------------------------------------------------------------
//         void CflowdCisco::GetInterfaceAddresses(Snmp & session, 
//                                                 CTarget & target) 
//............................................................................
//  
//----------------------------------------------------------------------------
void CflowdCisco::GetInterfaceAddresses(Snmp & session,
                                        CTarget & target)
{
  Vb   varBinding;
  Pdu  pdu;
  int  status;
  Oid  receivedOid;
  int  interfaceIndex;
  CflowdCiscoFlowInterfaceMap::iterator  intfMapIter;
  
  varBinding.set_oid(g_ipAdEntIfIndexOid);
  pdu += varBinding;

  while ((status = session.get_next(pdu,target)) == SNMP_CLASS_SUCCESS) {
    for (int i = 0; i < pdu.get_vb_count(); i++) {
      pdu.get_vb(varBinding,i);
      varBinding.get_oid(receivedOid);
      if (g_ipAdEntIfIndexOid.nCompare(g_ipAdEntIfIndexOid.len(),
                                       receivedOid) == 0) {
        int ifIndex;
        varBinding.get_value(interfaceIndex);
        ifIndex = interfaceIndex;
        intfMapIter = this->_interfaces.find(ifIndex);
        if (intfMapIter != this->_interfaces.end()) {
          intfMapIter->second.IpAddr(IpAdEntIfIndexOidIpAddr(receivedOid));
        }
      } else {
        pdu.delete_vb(i);
      }
    }
    if (pdu.get_vb_count() < 1)
      break;
  }
  return;  
}

//----------------------------------------------------------------------------
//                   int CflowdCisco::GetInterfaceInfo() 
//............................................................................
//  
//----------------------------------------------------------------------------
int CflowdCisco::GetInterfaceInfo()
{
  struct in_addr  inAddr;

  inAddr.s_addr = this->_ipAddress;

  GenAddress  snmpAddress(inet_ntoa(inAddr));
  CTarget     snmpTarget(snmpAddress,this->_snmpCommunity.c_str(),
                         this->_snmpCommunity.c_str());
  snmpTarget.set_timeout(200);
  snmpTarget.set_retry(2);
  if (! snmpTarget.valid()) {
    syslog(LOG_ERR,"[E] invalid SNMP target {%s:%d}",__FILE__,__LINE__);
    return(-1);
  }

  int   sessionStatus;
  Snmp  snmpSession(sessionStatus);
  if (sessionStatus) {
    syslog(LOG_ERR,"[E] failed to create SNMP session {%s:%d}",
           __FILE__,__LINE__);
    return(-1);
  }
  
  GetInterfaceDescriptions(snmpSession,snmpTarget);
  GetInterfaceAddresses(snmpSession,snmpTarget);

  this->_haveInterfaceInfo = true;
  this->_lastSnmpQueryTime = time((time_t *)0);
  
  return(0);
}

//----------------------------------------------------------------------------
//            bool CflowdCisco::HaveRecentInterfaceInfo() const 
//............................................................................
//  
//----------------------------------------------------------------------------
bool CflowdCisco::HaveRecentInterfaceInfo() const
{
  if (this->_haveInterfaceInfo &&
      (this->_lastSnmpQueryTime > (time((time_t *)0) - (30 * 60))))
    return(true);
  return(false);
}

//----------------------------------------------------------------------------
//          bool CflowdCisco::HaveRecentInterfaceInfo(bool value) 
//............................................................................
//  
//----------------------------------------------------------------------------
bool CflowdCisco::HaveRecentInterfaceInfo(bool value)
{
  if (! value)
    this->_lastSnmpQueryTime = 0;
  
  this->_haveInterfaceInfo = value;
  return(value);
}

const uint16_t CflowdCisco::k_cflowdProtocolTableMask      = 0x0001;
const uint16_t CflowdCisco::k_cflowdPortTableMask          = 0x0002;
const uint16_t CflowdCisco::k_cflowdNetMatrixMask          = 0x0004;
const uint16_t CflowdCisco::k_cflowdAsMatrixMask           = 0x0008;
const uint16_t CflowdCisco::k_cflowdRawFlowMask            = 0x0010;
const uint16_t CflowdCisco::k_cflowdPortMatrixMask         = 0x0020;
const uint16_t CflowdCisco::k_cflowdInterfaceMatrixMask    = 0x0040;
const uint16_t CflowdCisco::k_cflowdNextHopTableMask       = 0x0080;
const uint16_t CflowdCisco::k_cflowdTosTableMask           = 0x0100;

