#include #include #include #include #include #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()); _fd[0] = -1; _fd[1] = -1; // Setup parameters Params::convert(_params, param); //_params.log(); // 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 bpf_u_int32 mask; // subnet mask bpf_u_int32 net; // ip address 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); // Open the device _device = pcap_open_live(_params[Params::nic].c_str(), 65536, 1, 1000, error_buffer); // TODO Replace hard coded values by PcapLayer:: if (_device == NULL) { 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); } // else, continue // 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 } else { loggers::get_instance().error("PcapLayer::PcapLayer: Failed to open PCAP file %s", error_buffer); } } // Setup filter std::string filter = ""; if (!_params[Params::mac_src].empty()) { filter = " not ether src " + _params[Params::mac_src]; } it = _params.find(std::string("filter")); if ((it != _params.end()) && !it->second.empty()) { if (!filter.empty()) { filter += " and "; } filter += _params["filter"]; } // else nothing to do // Skip our own MAC address 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"); } if (pcap_setfilter(_device, &f) != 0) { loggers::get_instance().error("PcapLayer::PcapLayer: Failed to set PCAP filter"); } pcap_freecode(&f); } // Pass the device file handler to the polling procedure if (_pcap_h != -1) { // Live capture Handler_Add_Fd_Read(_pcap_h); } else { // Offline capture // Create a pipe if (pipe2(_fd, O_NONBLOCK) == -1) { loggers::get_instance().error("PcapLayer::PcapLayer: Failed to create a pipe: %s", ::strerror(errno)); } // Pass the pipe handler to the polling procedure loggers::get_instance().log("PcapLayer::PcapLayer: Call handler with descriptor %d", _fd[0]); Handler_Add_Fd_Read(_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"); } // Start it to dispatch packet to a pipe while (_running == FALSE) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); } // Thread was started loggers::get_instance().log("<<< PcapLayer::PcapLayer"); } } // End of ctor PcapLayer::~PcapLayer() { loggers::get_instance().log(">>> PcapLayer::~PcapLayer"); if (_device != NULL) { if (_thread != NULL) { _running = FALSE; // Wait for the working thread to terminate _thread->join(); loggers::get_instance().log("PcapLayer::~PcapLayer: Thread were stops"); // Cleanup delete _thread; close(_fd[0]); close(_fd[1]); } if (_sent_file != NULL) { pcap_dump_close(_sent_file); } pcap_close(_device); } } // End of dtor void* PcapLayer::run(void* p_this) { loggers::get_instance().log(">>> PcapLayer::run"); // Pointer the main object PcapLayer& p = *static_cast(p_this); // Wait a little bit before to start sending packet std::this_thread::sleep_for(std::chrono::milliseconds(500)); 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) { }*/ } // Let's go p._running = TRUE; while (p._running) { // Loop while _running flag is up if (p._resume.try_lock() == TRUE) { // Previous packet was consumed, lock for the next one write(p._fd[1], "\n", 1); // Any character will do the job } else { // not ready yet std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } loggers::get_instance().log("<<< PcapLayer::run"); return NULL; } void PcapLayer::sendData(OCTETSTRING& data, Params& params) { loggers::get_instance().log_msg(">>> PcapLayer::sendData: ", data); if (_pcap_h != -1) { // Check if offline mode is used if (pcap_sendpacket(_device, static_cast(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(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(data)); } else { 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); // Pass the packet to the upper layers receiveToAllLayers(data, params); } void PcapLayer::Handle_Fd_Event_Readable(int fd) { //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("timestamp"), std::to_string(pkt_header->ts.tv_usec))); // Process the packet at this layer OCTETSTRING os(pkt_header->caplen, pkt_data); //loggers::get_instance().log_to_hexa("PcapLayer::Handle_Fd_Event_Readable: ", os); // TODO Case of caplen != len !!! float duration; loggers::get_instance().set_start_time(_time_key); this->receiveData(os, params); // TODO Check execution time for decoding operation loggers::get_instance().set_stop_time(_time_key, duration); } } // 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(); } } // else, nothing to do } class PcapFactory: public LayerFactory { static PcapFactory _f; public: PcapFactory(); virtual Layer * createLayer(const std::string & type, const std::string & param); }; PcapFactory::PcapFactory() { // register factory loggers::get_instance().log(">>> PcapFactory::PcapFactory"); LayerStackBuilder::RegisterLayerFactory("PCAP", this); } Layer * PcapFactory::createLayer(const std::string & type, const std::string & param) { return new PcapLayer(type, param); } PcapFactory PcapFactory::_f;