Commit 685161c9 authored by filatov's avatar filatov
Browse files

add cygwin pcap layer

automatic local MAC address determination
parent 3e0dd4e8
Loading
Loading
Loading
Loading
+262 −0
Original line number Diff line number Diff line
#if defined (__CYGWIN__)

#define _GNU_SOURCE

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <chrono>
#include <sstream>
#include <iomanip>

#include <Port.hh>

#include "pcap_layer_factory.hh"

#include "loggers.hh"

#include <pcap.h>

typedef struct {
    bpf_int32 tv_sec;		/* seconds */
    bpf_int32 tv_usec;		/* microseconds */
}pcap_o_timeval;

typedef struct pcap_o_pkthdr { 
    pcap_o_timeval ts;	/* time stamp */
    bpf_u_int32 caplen;		/* length of portion present */
    bpf_u_int32 len;		/* length this packet (off wire) */
}pcap_o_pkthdr;

extern "C" int pcap_oid_get_request(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp);

static const char* _hexDigits = "0123456789ABCDEF";
static char * _bin2hex(char * hex, size_t hlen, const char * bin, size_t blen)
{
	const unsigned char *b, *e;
	char * s;

	// sanity check
	if (hlen >= 0 && hlen < blen * 2) return NULL;

	b = (const unsigned char *)bin;
	e = b + blen - 1;
	s = hex + blen * 2;
	if (s < hex + hlen) *s = 0;
	for (; b <= e; e--){
		*(--s) = _hexDigits[(*e) & 0xF];
		*(--s) = _hexDigits[(*e) >> 4];
	}
	return hex + blen * 2;
}

pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) : 
	layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_layer::Handle_Fd_Event_Readable") {
  loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", p_type.c_str(), param.c_str());
  params::convert(_params, param);

  char error_buffer[PCAP_ERRBUF_SIZE];
  params::const_iterator it;
  std::string nic;  //network interface name
  bpf_u_int32 mask; // subnet mask
  bpf_u_int32 net;  // ip address

  it = _params.find(params::nic); 
  if ((it == _params.end()) || it->second.empty()) { 
    loggers::get_instance().error("pcap_layer::pcap_layer: NIC must be specified");
    return;
  }

  nic = std::string("\\Device\\NPF_{") +  it->second + "}";

  if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) {
    loggers::get_instance().error("pcap_layer::pcap_layer: pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str());
  }
  loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net);
  
  // Open the device
  _device = pcap_open_live(nic.c_str(), 65536, 1, 200, error_buffer);
  if (_device == NULL) {
    loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str());
    return;
  }
  
  // Setup filter
  std::string filter = "";
  std::string mac_src;
  it = _params.find(params::mac_src);
  if (it != _params.end() && !it->second.empty()) {
    mac_src = it->second;
  } else {
    // Not found
    // determine it automatically
#ifndef OID_802_3_CURRENT_ADDRESS
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#endif
    char mac[6] = {0};
    size_t l = sizeof(mac);
    pcap_oid_get_request(_device, OID_802_3_CURRENT_ADDRESS, mac, &l);
    char buf[13];
    *_bin2hex(buf, sizeof(buf), mac, 6) = 0;
    mac_src = buf;
    loggers::get_instance().user("pcap_layer::pcap_layer: local MAC is %s", mac_src.c_str());
  }
  
  std::string mac_bc;
  it = _params.find(params::mac_bc);
  if (it != _params.end() && !it->second.empty())
	mac_bc = it->second;
  else
	mac_bc = "ffffffffffff";

  if(mac_bc == mac_src || mac_src.empty())
    filter = "ether dst " + mac_bc;
  else
	filter = "( ether dst " + mac_bc + " or ether dst " + mac_src + " )";

  if(! mac_src.empty())
    // Reject ITS messages sent by this component
    filter += " and not ether src " + mac_src;

  // Add user defined filter
  it = _params.find(std::string("filter"));
  if ((it != _params.end()) && !it->second.empty()) {
    filter += std::string(" ") + it->second;
  }
  
  // Log final PCAP filter
  loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str());
  
  // setup filter
  {
    struct bpf_program f = {0};
    if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) {
      loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter");
    } else {
      if (pcap_setfilter(_device, &f) != 0) {
        loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter");
      }
    }
    pcap_freecode(&f);
  }

  _o_params.insert(std::pair<std::string, std::string>(std::string("timestamp"), std::string()));

  // create pipe and run thread
  if (pipe2(_fd, O_NONBLOCK) == -1) {
    loggers::get_instance().error("pcap_layer::pcap_layer: Failed to create a pipe: %s", ::strerror(errno));
  }
  // Pass the pipe handler to the polling procedure
  loggers::get_instance().log("pcap_layer::pcap_layer: Call handler with descriptor %d", _fd[0]);
  Handler_Add_Fd_Read(_fd[0]);
  
  // Create the reader thread
  _thread = new std::thread(&pcap_layer::run, (void *)this);
  if (_thread == NULL) {
    loggers::get_instance().error("pcap_layer::pcap_layer: Failed to start offline thread");
  }
  while (_running == FALSE) {
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
  }
  // Thread was started
  loggers::get_instance().log("<<< pcap_layer::pcap_layer");
} // End of ctor

pcap_layer::~pcap_layer() {
  loggers::get_instance().log(">>> pcap_layer::~pcap_layer");
  
  if (_device != NULL) {
    if (_thread != NULL) {
      _running = FALSE;
      // Wait for the working thread to terminate
      _thread->join();
      loggers::get_instance().log("pcap_layer::~pcap_layer: Thread were stops");
      // Cleanup
      delete _thread;
      close(_fd[0]);
      close(_fd[1]);
    }
    pcap_close(_device);
  }
} // End of dtor

void* pcap_layer::run(void* p_this) {
  pcap_layer& p = *static_cast<pcap_layer *>(p_this);
  return p.thread();
}

void* pcap_layer::thread() {
  pcap_o_pkthdr *pkt_header;
  const u_char *pkt_data;
  unsigned char pkt_count = 0;

  loggers::get_instance().log(">>> pcap_layer::run");

  _running = TRUE;

  // wait a bit before sending first packet
  std::this_thread::sleep_for(std::chrono::milliseconds(500));

  while (_running) { // Loop while _running flag is up
    // get next frame
    int result = pcap_next_ex(_device, (struct pcap_pkthdr**)&pkt_header, &pkt_data);
    if(result == 0){
      continue;
    }
    if(result < 0){
      loggers::get_instance().log("<<< pcap_layer::run: error %s", pcap_geterr(_device));
      break;
    }

    while(_running && !_resume.try_lock()) {
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
#if 0
    {
      char buf[128];
      std::time_t t = pkt_header->ts.tv_sec;
      std::tm * pt = std::localtime( &t );
      t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt);
      std::sprintf(buf+t, ".%06ld", pkt_header->ts.tv_usec);
      _o_params["timestamp"] = std::string(buf);
    }
#else
    _o_params["timestamp"] = std::to_string(pkt_header->ts.tv_usec);
#endif
    _o_data = OCTETSTRING(pkt_header->len, pkt_data);
    write(_fd[1], &pkt_count, 1);pkt_count++;
  }
  _running = FALSE;
  loggers::get_instance().log("<<< pcap_layer::run");
  return NULL;
}

void pcap_layer::send_data(OCTETSTRING& data, params& params) {
  loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data);
  if (pcap_sendpacket(_device, static_cast<const unsigned char *>(data), data.lengthof()) == -1) {
    loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device));
  }
}

void pcap_layer::receive_data(OCTETSTRING& data, params& params) {
  loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof());
  loggers::get_instance().log_to_hexa("Packet dump", data);
  
  // Pass the packet to the upper layers
  receive_to_all_layers(data, params);
}

void pcap_layer::Handle_Fd_Event_Readable(int fd) {
  char c[2];
  float duration;
  loggers::get_instance().set_start_time(_time_key);
  this->receive_data(_o_data, _o_params);
  loggers::get_instance().set_stop_time(_time_key, duration);
  read(_fd[0], &c, 1);
  _resume.unlock();
}

pcap_layer_factory pcap_layer_factory::_f;

#endif //__CYGWIN__
+78 −0
Original line number Diff line number Diff line
/*!
 * \file      pcap_layer.hh
 * \brief     Header file for ITS Offline Pcap port layer.
 * \author    ETSI STF525
 * \copyright ETSI Copyright Notification
 *            No part may be reproduced except as authorized by written permission.
 *            The copyright and the foregoing restriction extend to reproduction in all media.
 *            All rights reserved.
 * \version   0.1
 */
#pragma once

#include <thread>
#include <mutex>

#include "t_layer.hh"
#include "params.hh"

#include <Octetstring.hh>

class PORT; //! Forward declaration of TITAN class

typedef struct pcap pcap_t;

/*!
 * \class pcap_layer
 * \brief  This class provides description of ITS PCAP port protocol layer
 */
class pcap_layer : public layer, public PORT {

  params _params;            //! Layer parameters
  pcap_t* _device;           //! Device handle
  std::thread* _thread;      //! Thread handle, used to read PCAP file instead of NIC, used in file mode
  std::mutex _resume;
  bool _running;             //! Set to true when the thread is running, used in file mode
  int _fd[2];                //! pipe to signal to Titan


  OCTETSTRING _o_data;
  params      _o_params;

  std::string _time_key;

  static void* run(void* p_this);
public: 
  void* thread(void);
public: //! \publicsection
  /*!
   * \brief Specialised constructor
   *        Create a new instance of the pcap_layer class
   * \param[in] p_type \todo
   * \param[in] p_param \todo
   */
  pcap_layer(const std::string& p_type, const std::string& param);
  /*!
   * \brief Default destructor
   */
  virtual ~pcap_layer();

  /*!
   * \virtual
   * \fn void send_data(OCTETSTRING& data, params& params);
   * \brief Send bytes formated data to the lower layers
   * \param[in] p_data The data to be sent
   * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters
   */
  virtual void send_data(OCTETSTRING& data, params& params);
  /*!
   * \virtual
   * \fn void receive_data(OCTETSTRING& data, params& params);
   * \brief Receive bytes formated data from the lower layers
   * \param[in] p_data The bytes formated data received
   * \param[in] p_params Some lower layers parameters values when data was received
   */
  virtual void receive_data(OCTETSTRING& data, params& info);
  
  void Handle_Fd_Event_Readable(int fd);
};
+2 −50
Original line number Diff line number Diff line
#if defined (__CYGWIN__)
#define _GNU_SOURCE
#endif
#if 0
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -11,27 +9,8 @@

#include "pcap_layer_factory.hh"

#if defined (__CYGWIN__)
typedef void* HANDLE;
typedef unsigned int DWORD;
extern "C" {
DWORD WaitForSingleObject(HANDLE hHandle, DWORD  dwMilliseconds);
HANDLE pcap_getevent(pcap_t *p);
}
typedef struct {
    bpf_int32 tv_sec;		/* seconds */
    bpf_int32 tv_usec;		/* microseconds */
}pcap_o_timeval;

typedef struct pcap_o_pkthdr {
    pcap_o_timeval ts;	/* time stamp */
    bpf_u_int32 caplen;		/* length of portion present */
    bpf_u_int32 len;		/* length this packet (off wire) */
}pcap_o_pkthdr;
#else
typedef struct pcap_pkthdr pcap_o_pkthdr;
typedef struct timeval pcap_o_timeval;
#endif

#include "loggers.hh"

@@ -51,11 +30,7 @@ pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) : la
    bpf_u_int32 net; // ip address
    std::string nic;
    online = true;
#ifdef __CYGWIN__
    nic = std::string("\\Device\\NPF_{") +  _params[params::nic] + "}";
#else
    nic = _params[params::nic];
#endif
    if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) {
      loggers::get_instance().error("pcap_layer::pcap_layer: pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str());
    }
@@ -69,13 +44,11 @@ pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) : la
    if (pcap_setnonblock(_device, 1, error_buffer) != 0) {
      loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set blocking mode: %s", error_buffer);
    }
#if ! defined (__CYGWIN__)
    // Retrieve the device file handler
    _pcap_h = pcap_get_selectable_fd(_device);
    if (_pcap_h == -1) {
      loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device handler");
    }
#endif
  } else {
    // Check file name
    it = _params.find(std::string("file"));
@@ -220,28 +193,6 @@ void* pcap_layer::thread() {
      catch (out_of_range& o) {
      }*/
  }
#if defined (__CYGWIN__)
  // check HANDLE emulation for pcap 
  if(_fd[0] == _pcap_h) {
    HANDLE h = pcap_getevent(_device);
    _running = TRUE;
    while (_running) { // Loop while _running flag is up
      DWORD rc = WaitForSingleObject(h, 1000);
      if(rc == 0){
        // event raised
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        if (_resume.try_lock() == TRUE) { // Previous packet was consumed, lock for the next one
          write(_fd[1], "\n", 1); // Any character will do the job
        } else { // not ready yet
          std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
      }else if(rc == 0x00000102L) { // timeout
      }else {
      }
    }
    return NULL;
  }
#endif
  // Let's go
  _running = TRUE;
  while (_running) { // Loop while _running flag is up
@@ -322,3 +273,4 @@ void pcap_layer::Handle_Fd_Event_Readable(int fd) {

pcap_layer_factory pcap_layer_factory::_f;

#endif // !CYGWIN
+4 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ typedef struct timeval pcap_o_timeval;
#endif

pcap_offline_layer::pcap_offline_layer(const std::string& p_type, const std::string& param) : 
	layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE) {
	layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_offline_layer::Handle_Fd_Event_Readable") {
  loggers::get_instance().log(">>> pcap_offline_layer::pcap_offline_layer: %s, %s", p_type.c_str(), param.c_str());
  params::convert(_params, param);

@@ -209,8 +209,11 @@ void pcap_offline_layer::receive_data(OCTETSTRING& data, params& params) {
void pcap_offline_layer::Handle_Fd_Event_Readable(int fd) {
  //loggers::get_instance().log(">>> pcap_offline_layer::Handle_Fd_Event_Readable: %d", fd);
  char c[2];
  float duration;
  // Process the packet at this layer
  loggers::get_instance().set_start_time(_time_key);
  this->receive_data(_o_data, _o_params);
  loggers::get_instance().set_stop_time(_time_key, duration);
  read(_fd[0], &c, 1);
  _resume.unlock();
}
+2 −0
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ class pcap_offline_layer : public layer, public PORT {
  params                _o_params;
  OCTETSTRING           _o_data;

  std::string           _time_key;

  static void* run(void* p_this);
public: 
  void* thread(void);