Newer
Older
#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"
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;
// 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)) {
loggers::loggers::error("PcapLayer::PcapLayer: Failed to acces PCAP file %s", _params["file"].c_str());
}
// File exist, open it
_device = pcap_open_offline(_params["file"].c_str(), error_buffer);
if (_device == NULL) {
loggers::loggers::error("PcapLayer::PcapLayer: Failed to open PCAP file %s", error_buffer);
} // 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) {
} 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;
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??
}
void PcapLayer::receiveData(OCTETSTRING& data, Params& params) {
//loggers::loggers::log(">>> PcapLayer::receiveData: Received %d bytes", data.lengthof());
loggers::loggers::log_to_hexa("Packet dump", data);
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
//loggers::loggers::log_to_hexa("PcapLayer::Handle_Fd_Event_Readable: ", os);
// TODO Case of caplen != len !!!
this->receiveData(os, params); // TODO Check execution time for decoding operation
}
} // 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
//loggers::loggers::log("PcapLayer::Handle_Fd_Event_Readable: pcap_next_ex failed: result=%d", result);
}
class PcapFactory: public LayerFactory {
PcapFactory();
virtual Layer * createLayer(const std::string & type,
const std::string & param);
// register factory
loggers::loggers::log(">>> PcapFactory::PcapFactory");
LayerStackBuilder::RegisterLayerFactory("PCAP", this);
}
Layer * PcapFactory::createLayer(const std::string & type, const std::string & param) {