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) {
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
} 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);
// Pass the packet to the upper layers
toAllUpperLayers(data, params);
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
OCTETSTRING os(pkt_header->caplen, pkt_data);
this->receiveData(os, params);
}
} // 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) {