PcapLayer.cc 7.95 KB
Newer Older
garciay's avatar
garciay committed
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <thread>
#include <chrono>

#include "Port.hh"

#include "PcapLayer.hh"
#include "loggers.hh"

garciay's avatar
garciay committed
PcapLayer::PcapLayer(const std::string & p_type, const std::string & param) : Layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _pcap_h(-1), _thread_id(-1), _running(FALSE), _resume(FALSE) {
  loggers::loggers::log(">>> PcapLayer::PcapLayer: %s, %s", to_string().c_str(), param.c_str());
  _fd[0] = -1; _fd[1] = -1;
garciay's avatar
garciay committed
  // Setup parameters
  Params::convert(_params, param);
garciay's avatar
garciay committed
  //_params.log();
garciay's avatar
garciay committed
  // Prepare capture processing
  char error_buffer[PCAP_ERRBUF_SIZE];
  std::map<std::string, std::string>::const_iterator it = _params.find(std::string("nic")); 
  if ((it != _params.end()) && !it->second.empty()) { // Use online capture
    // Fetch the network address and network mask
    /*bpf_u_int32 mask; // subnet mask
    bpf_u_int32 net; // ip address
    if (pcap_lookupnet(_params["nic"].c_str(), &net, &mask, error_buffer) != 0) {
      loggers::loggers::error("PcapLayer::PcapLayer: PcapLayer::PcapLayer: Failed to fetch newtork address for device %s", _params["nic"].c_str());
    }
    loggers::loggers::log("PcapLayer::PcapLayer: Device %s Network address: %d", _params["nic"].c_str(), net);*/
    // Open the device
    _device = pcap_open_live(_params["nic"].c_str(), 65536, 1, 1000, error_buffer); // TODO Replace hard coded values by PcapLayer::<constants>
    if (_device == NULL) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to open device %s", _params["nic"].c_str());
    } // else, continue
    // Set non-blocking flag for the polling procedure
    if (pcap_setnonblock(_device, 1, error_buffer) != 0) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to set blocking mode: %s", error_buffer);
    }
    // Retrieve the device file handler
    _pcap_h = pcap_get_selectable_fd(_device);
    if (_pcap_h == -1) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to get device handler");
    }
  } else {
    // Check file name
    it = _params.find(std::string("file"));
    if ((it != _params.end()) && !it->second.empty()) { // Use offline capture
      struct stat s = {0};
      if ((stat(_params["file"].c_str(), &s) != 0) || !S_ISREG(s.st_mode)) {
garciay's avatar
garciay committed
	loggers::loggers::error("PcapLayer::PcapLayer: Failed to acces PCAP file %s", _params["file"].c_str());
garciay's avatar
garciay committed
      }
      // File exist, open it
      _device = pcap_open_offline(_params["file"].c_str(), error_buffer);
      if (_device == NULL) {
garciay's avatar
garciay committed
	loggers::loggers::error("PcapLayer::PcapLayer: Failed to open PCAP file %s", error_buffer);	
garciay's avatar
garciay committed
      } // else, continue
      
    } else {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to open PCAP file %s", error_buffer);
    }
  }
  // Setup filter
  std::string filter;
  it = _params.find(std::string("filter"));
  if ((it != _params.end()) && !it->second.empty()) {
    filter = _params["filter"];
  } // else nothing to do
  loggers::loggers::log("PcapLayer::PcapLayer: Filter: %s", filter.c_str());
  if (!filter.empty()) {
    struct bpf_program f = {0};
    if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to compile PCAP filter");
    }
    if (pcap_setfilter(_device, &f) != 0) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to set PCAP filter");
    }
    pcap_freecode(&f);
  }
  // Pass the device file handler to the polling procedure
  if (_pcap_h != -1) {
    Handler_Add_Fd_Read(_pcap_h);
garciay's avatar
garciay committed
  } else {
    // Create a pipe
    if (pipe2(_fd, O_NONBLOCK) == -1) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to create a pipe: %s", ::strerror(errno));
    }
    // Pass the pipe handler to the polling procedure
    loggers::loggers::log("PcapLayer::PcapLayer: Call handler with descriptor %d", _fd[0]);
    Handler_Add_Fd_Read(_fd[0]);
    if (pthread_create(&_thread_id, NULL, &PcapLayer::run, (void *)this) != 0) {
      loggers::loggers::error("PcapLayer::PcapLayer: Failed to compile PCAP filter");
    }
    // Start a working thread to dispatch packet to a pipe
    while (_running == FALSE) {
      std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    loggers::loggers::log("<<< PcapLayer::PcapLayer");    
  }
} // End of ctor

PcapLayer::~PcapLayer() {
  loggers::loggers::log(">>> PcapLayer::~PcapLayer");
  if (_device != NULL) {
    if (_fd[0] != -1) { // TODO To be refined :-(
      _running = FALSE;
      // Wait for the working thread to terminate
      pthread_join(_thread_id, NULL);
      loggers::loggers::log("PcapLayer::~PcapLayer: Thread were stops");
      // Cleanup
      close(_fd[0]);
      close(_fd[1]);
    }
    pcap_close(_device);
  }
} // End of dtor

void * PcapLayer::run(void *p_this) {
  loggers::loggers::log(">>> PcapLayer::run: Class pointer: %p", p_this);
  PcapLayer& p = *static_cast<PcapLayer *>(p_this);
  p._running = TRUE;
  p._resume = TRUE;
  while (p._running) {
    if (p._resume == TRUE) {
      write(p._fd[1], "\n", 1);
      p._resume = FALSE;
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
  }
  loggers::loggers::log("<<< PcapLayer::run");
  return NULL;
garciay's avatar
garciay committed
void PcapLayer::sendData(OCTETSTRING& data, Params& params) {
garciay's avatar
garciay committed
  loggers::loggers::log_msg(">>> PcapLayer::sendData: ", data);
garciay's avatar
garciay committed

  if (_pcap_h != -1) { // Check if offline mode is used
    if (pcap_inject(_device, static_cast<const unsigned char *>(data), data.lengthof()) != 0) {
      loggers::loggers::error("PcapLayer::sendData: Failed to send packet: %s", pcap_geterr(_device));
    }
  } else {
    loggers::loggers::log("PcapLayer::sendData: Offline mode, operation was skipped");
    // TODO Use PCAP dump file to store sent message in a file??
  }
garciay's avatar
garciay committed
}

garciay's avatar
garciay committed
void PcapLayer::receiveData(OCTETSTRING& data, Params& params) {
  loggers::loggers::log(">>> PcapLayer::receiveData: Received %d bytes", data.lengthof());
garciay's avatar
garciay committed
  //loggers::loggers::log_to_hexa("Packet dump", data);
  // Pass the packet to the upper layers
  toAllUpperLayers(data, params);
garciay's avatar
garciay committed
void PcapLayer::Handle_Fd_Event_Readable(int fd) {
  //loggers::loggers::log(">>> PcapLayer::Handle_Fd_Event_Readable: %d", fd);
  
  struct pcap_pkthdr *pkt_header;
  const u_char *pkt_data;
  int result = pcap_next_ex(_device, &pkt_header, &pkt_data);
  if (result == 1) { // Succeed
    if (pkt_header->caplen > 14) { // Reject too small packet
      //loggers::loggers::log("PcapLayer::Handle_Fd_Event_Readable: %.6d - %d", pkt_header->ts.tv_usec, pkt_header->len);
      // Fill parameters from PCAP layer
      Params params;
      params.insert(std::pair<std::string, std::string>(std::string("timestamp"), std::to_string(pkt_header->ts.tv_usec)));
      // Process the packet at this layer
garciay's avatar
garciay committed
      OCTETSTRING os(pkt_header->caplen, pkt_data);
      this->receiveData(os, params);
garciay's avatar
garciay committed
    }
  } // else, skip the packet
  // Specific to offline mode
  if (_fd[0] != -1) { // Check if offline mode is used
    //loggers::loggers::log("PcapLayer::Handle_Fd_Event_Readable: Read pipe");
    char c[2];
    read(_fd[0], &c, 1);
    if (result == -2) { // End of file, therminate worker thread
      _running = FALSE;
    } else { // Get next packet
garciay's avatar
garciay committed
      //loggers::loggers::log("PcapLayer::Handle_Fd_Event_Readable: pcap_next_ex failed: result=%d", result);
garciay's avatar
garciay committed
      _resume = TRUE;
    }
  } // else, nothing to do
}

class PcapFactory: public LayerFactory {
garciay's avatar
garciay committed
  static PcapFactory _f;
public:
garciay's avatar
garciay committed
  PcapFactory();
  virtual Layer * createLayer(const std::string & type,
			      const std::string & param);
};

PcapFactory::PcapFactory() {
garciay's avatar
garciay committed
  // register factory
  loggers::loggers::log(">>> PcapFactory::PcapFactory");
  LayerStackBuilder::RegisterLayerFactory("PCAP", this);
}

Layer * PcapFactory::createLayer(const std::string & type, const std::string & param) {
garciay's avatar
garciay committed
  return new PcapLayer(type, param);
}

PcapFactory PcapFactory::_f;