Newer
Older
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#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(NULL), _running(FALSE), _resume(), _sent_file(NULL), _time_key("PcapLayer::Handle_Fd_Event_Readable") {
loggers::get_instance().log(">>> PcapLayer::PcapLayer: %s, %s", to_string().c_str(), param.c_str());
// Prepare capture processing
char error_buffer[PCAP_ERRBUF_SIZE];
Params::const_iterator it = _params.find(Params::nic);
if ((it != _params.end()) && !it->second.empty()) { // Use online capture
// Fetch the network address and network mask
if (pcap_lookupnet(_params[Params::nic].c_str(), &net, &mask, error_buffer) != 0) {
loggers::get_instance().error("PcapLayer::PcapLayer: PcapLayer::PcapLayer: Failed to fetch newtork address for device %s", _params[Params::nic].c_str());
loggers::get_instance().log("PcapLayer::PcapLayer: Device %s Network address: %d", _params[Params::nic].c_str(), net);
_device = pcap_open_live(_params[Params::nic].c_str(), 65536, 1, 1000, error_buffer); // TODO Replace hard coded values by PcapLayer::<constants>
loggers::get_instance().error("PcapLayer::PcapLayer: Failed to open device %s", _params[Params::nic].c_str());
} // else, continue
// Set non-blocking flag for the polling procedure
if (pcap_setnonblock(_device, 1, error_buffer) != 0) {
loggers::get_instance().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::get_instance().error("PcapLayer::PcapLayer: Failed to get device handler");
}
} else {
// Check file name
it = _params.find(std::string("file"));
if ((it != _params.cend()) && !it->second.empty()) { // Use offline capture
struct stat s = {0};
if ((stat(_params["file"].c_str(), &s) != 0) || !S_ISREG(s.st_mode)) {
loggers::get_instance().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::get_instance().error("PcapLayer::PcapLayer: Failed to open PCAP file %s", error_buffer);
// Create the dump file for the sent packet based on the openned file name and the current time in milliseconds
it = _params.find("save_mode");
if ((it != _params.cend()) && (it->second.compare("1") == 0)) {
unsigned long ms = std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
std::string ext("_" + std::to_string(ms));
int i = _params["file"].find(".pcap");
if (i > 0) {
std::string f(_params["file"].substr(0, i) + ext + ".pcap");
loggers::get_instance().log("PcapLayer::PcapLayer: Save file name: %s", f.c_str());
if ((_sent_file = pcap_dump_open(_device, f.c_str())) == NULL) {
loggers::get_instance().warning("PcapLayer::PcapLayer: Failed to open save file %s", f.c_str());
}
}
} // else, nothing to do
loggers::get_instance().error("PcapLayer::PcapLayer: Failed to open PCAP file %s", error_buffer);
std::string filter(" not ether src " + _params[Params::mac_src]);
it = _params.find(std::string("filter"));
if ((it != _params.end()) && !it->second.empty()) {
filter += " and " + _params["filter"];
loggers::get_instance().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::get_instance().error("PcapLayer::PcapLayer: Failed to compile PCAP filter");
loggers::get_instance().error("PcapLayer::PcapLayer: Failed to set PCAP filter");
}
pcap_freecode(&f);
}
// Pass the device file handler to the polling procedure
loggers::get_instance().error("PcapLayer::PcapLayer: Failed to create a pipe: %s", ::strerror(errno));
loggers::get_instance().log("PcapLayer::PcapLayer: Call handler with descriptor %d", _fd[0]);
// Create the offline reader thread
_thread = new std::thread(&PcapLayer::run, (void *)this);
if (_thread == NULL) {
loggers::get_instance().error("PcapLayer::PcapLayer: Failed to start offline thread");
while (_running == FALSE) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
_running = FALSE;
// Wait for the working thread to terminate
_thread->join();
loggers::get_instance().log("PcapLayer::~PcapLayer: Thread were stops");
if (_sent_file != NULL) {
pcap_dump_close(_sent_file);
}
loggers::get_instance().log(">>> PcapLayer::run");
// Pointer the main object
// Wait a little bit before to start sending packet
std::this_thread::sleep_for(std::chrono::seconds(1));
Params::const_iterator it = p._params.find("frame_offset");
if ((it != p._params.cend()) && (it->second.compare("0") != 0)) {
// TODO Try t use PCAP filter to start directly to the correct frame offset
/*try {
unsigned int offset = std::stoul(str_dec, &s);
// Skip frames
struct pcap_pkthdr *pkt_header;
const u_char *pkt_data;
int result = pcap_next_ex(_device, &pkt_header, &pkt_data);
if (result == 1) { // Succeed
}
}
catch (invalid_argument& i) {
}
catch (out_of_range& o) {
}*/
}
while (p._running) { // Loop while _running flag is up
if (p._resume.try_lock() == TRUE) { // Previous packet was consumed, lock for the next one
std::this_thread::sleep_for(std::chrono::milliseconds(100));
//loggers::get_instance().log_msg(">>> PcapLayer::sendData: ", data);
if (pcap_sendpacket(_device, static_cast<const unsigned char *>(data), data.lengthof()) == -1) {
loggers::get_instance().error("PcapLayer::sendData: Failed to send packet: %s", pcap_geterr(_device));
} else if (_sent_file != NULL) {
struct pcap_pkthdr hdr;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
hdr.ts.tv_sec = ms.count() / 1000;
hdr.ts.tv_usec = (ms.count() % 1000) * 1000;
hdr.caplen = data.lengthof();
hdr.len = hdr.caplen;
pcap_dump((u_char *)_sent_file, &hdr, static_cast<const unsigned char *>(data));
loggers::get_instance().log("PcapLayer::sendData: Offline mode, operation was skipped");
void PcapLayer::receiveData(OCTETSTRING& data, Params& params) {
//loggers::get_instance().log(">>> PcapLayer::receiveData: Received %d bytes", data.lengthof());
loggers::get_instance().log_to_hexa("Packet dump", data);
//loggers::get_instance().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::get_instance().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::get_instance().log_to_hexa("PcapLayer::Handle_Fd_Event_Readable: ", os);
float duration;
loggers::get_instance().set_start_time(_time_key);
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::get_instance().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::get_instance().log("PcapLayer::Handle_Fd_Event_Readable: pcap_next_ex failed: result=%d", result);
_resume.unlock();
}
class PcapFactory: public LayerFactory {
PcapFactory();
virtual Layer * createLayer(const std::string & type,
const std::string & param);
}
Layer * PcapFactory::createLayer(const std::string & type, const std::string & param) {