1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-29 13:04:28 +01:00

BaseSniffer::sniff_loop now uses begin() and end() to process packets, making it around 8% faster. Also added BaseSniffer::link_type to retrieve the pcap handle link type.

This commit is contained in:
Matias Fontanini
2013-09-15 12:16:54 -03:00
parent f8d71687e1
commit 20f3911e12
2 changed files with 72 additions and 142 deletions

View File

@@ -76,22 +76,20 @@ namespace Tins {
* This constructor is available only in C++11.
*/
BaseSniffer(BaseSniffer &&rhs) noexcept
: handle(nullptr), mask()
{
*this = std::move(rhs);
}
/**
* \brief Move assignment operator.
* This opeartor is available only in C++11.
* This operator is available only in C++11.
*/
BaseSniffer& operator=(BaseSniffer &&rhs) noexcept
{
handle = 0;
mask = rhs.mask;
iface_type = rhs.iface_type;
actual_filter.bf_insns = 0;
std::swap(handle, rhs.handle);
std::swap(actual_filter, rhs.actual_filter);
using std::swap;
swap(handle, rhs.handle);
swap(mask, rhs.mask);
return *this;
}
#endif
@@ -143,12 +141,12 @@ namespace Tins {
* \brief Starts a sniffing loop, using a callback object for every
* sniffed packet.
*
* The callback object must implement an operator with some of
* the following(or compatible) signatures:
* The callback object must implement an operator with one of the
* following signatures:
*
* \code
* bool operator()(PDU&);
* bool operator()(RefPacket&);
* bool operator()(const PDU&);
* \endcode
*
* This operator will be called using the sniffed packets
@@ -156,17 +154,29 @@ namespace Tins {
* Calling PDU methods like PDU::release_inner_pdu is perfectly
* valid.
*
* The callback taking a RefPacket will contain a timestamp
* indicating the moment in which the packet was taken out of
* the wire/pcap file.
*
* Note that the Functor object will be copied using its copy
* constructor, so that object should be some kind of proxy to
* another object which will process the packets(e.g. std::bind).
*
* Sniffing will stop when either max_packets are sniffed(if it is != 0),
* or when the functor returns false.
*
* This method catches both malformed_packet and pdu_not_found exceptions,
* which allows writing much cleaner code, since you can call PDU::rfind_pdu
* without worrying about catching the exception that can be thrown. This
* allows writing code such as the following:
*
* \code
* bool callback(const PDU& pdu) {
* // If either RawPDU is not found, or construction of the DNS
* // object fails, the BaseSniffer object will trap the exceptions,
* // so we don't need to worry about it.
* DNS dns = pdu.rfind_pdu<RawPDU>().to<DNS>();
* return true;
* }
* \endcode
*
* \sa RefPacket
*
* \param cback_handler The callback handler object which should process packets.
* \param function The callback handler object which should process packets.
* \param max_packets The maximum amount of packets to sniff. 0 == infinite.
*/
template<class Functor>
@@ -181,6 +191,9 @@ namespace Tins {
/**
* \brief Stops sniffing loops.
*
* This method must be called from the same thread from which
* BaseSniffer::sniff_loop was called.
*/
void stop_sniff();
@@ -189,6 +202,14 @@ namespace Tins {
*/
int get_fd();
/**
* \brief Retrieves this sniffer's link type.
*
* This calls pcap_datalink on the stored pcap handle and
* returns its result.
*/
int link_type() const;
/**
* Retrieves an iterator to the next packet in this sniffer.
*/
@@ -215,62 +236,16 @@ namespace Tins {
*/
void init(pcap_t *phandle, const std::string &filter, bpf_u_int32 if_mask);
private:
template<class Functor>
struct LoopData {
pcap_t *handle;
Functor c_handler;
int iface_type;
LoopData(pcap_t *_handle, const Functor _handler,
int if_type)
: handle(_handle), c_handler(_handler), iface_type(if_type)
{ }
};
struct PCapLoopBreaker {
bool &went_well;
pcap_t *handle;
void proxy_pcap_breakloop(pcap_t *);
PCapLoopBreaker(bool &went_well, pcap_t *handle)
: went_well(went_well), handle(handle) { }
~PCapLoopBreaker() {
if(!went_well)
proxy_pcap_breakloop(handle);
}
};
BaseSniffer(const BaseSniffer&);
BaseSniffer &operator=(const BaseSniffer&);
int proxy_pcap_loop(pcap_t *, int, pcap_handler, u_char *);
template<class ConcretePDU, class Functor>
static bool call_functor(LoopData<Functor> *data, const u_char *packet, const struct pcap_pkthdr *header);
bool compile_set_filter(const std::string &filter, bpf_program &prog);
template<class Functor>
static void callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
pcap_t *handle;
bpf_u_int32 mask;
bpf_program actual_filter;
int iface_type;
};
/**
* \class Sniffer
* \brief Sniffs packets using pcap filters.
*
* This class uses a given filter to sniff packets and allow the user
* to handle them. Each time a filter is set, it's used until a new one
* is set. Both Sniffer::next_packet and Sniffer::sniff_loop have an
* optional filter parameter. If a filter is set using those parameter,
* the previously set filter is freed and the new one is used.
* \brief Sniffs packets from a network interface.
*/
class Sniffer : public BaseSniffer {
public:
@@ -287,7 +262,7 @@ namespace Tins {
/**
* \class FileSniffer
* \brief Parses pcap files and interprets the packets in it.
* \brief Reads pcap files and interprets the packets in it.
*
* This class acts exactly in the same way that Sniffer, but reads
* packets from a pcap file instead of an interface.
@@ -301,59 +276,6 @@ namespace Tins {
*/
FileSniffer(const std::string &file_name, const std::string &filter = "");
};
template<class Functor>
void Tins::BaseSniffer::sniff_loop(Functor function, uint32_t max_packets) {
LoopData<Functor> data(handle, function, iface_type);
proxy_pcap_loop(handle, max_packets, &BaseSniffer::callback_handler<Functor>, (u_char*)&data);
}
template<class ConcretePDU, class Functor>
bool Tins::BaseSniffer::call_functor(LoopData<Functor> *data, const u_char *packet,
const struct pcap_pkthdr *header)
{
ConcretePDU some_pdu((const uint8_t*)packet, header->caplen);
Timestamp ts(header->ts);
RefPacket pck(some_pdu, ts);
return data->c_handler(pck);
}
template<class Functor>
void Tins::BaseSniffer::callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
bool ret_val(true);
LoopData<Functor> *data = reinterpret_cast<LoopData<Functor>*>(args);
PCapLoopBreaker _(ret_val, data->handle);
try {
if(data->iface_type == DLT_EN10MB) {
ret_val = Internals::is_dot3((const uint8_t*)packet, header->caplen) ?
call_functor<Tins::Dot3>(data, packet, header) :
call_functor<Tins::EthernetII>(data, packet, header);
}
else if(data->iface_type == DLT_IEEE802_11_RADIO)
ret_val = call_functor<Tins::RadioTap>(data, packet, header);
else if(data->iface_type == DLT_IEEE802_11) {
Internals::smart_ptr<PDU>::type pdu(
Tins::Dot11::from_bytes((const uint8_t*)packet, header->caplen)
);
if(pdu.get()) {
RefPacket pck(*pdu, header->ts);
ret_val = data->c_handler(pck);
}
}
else if(data->iface_type == DLT_NULL)
ret_val = call_functor<Tins::Loopback>(data, packet, header);
else if(data->iface_type == DLT_LINUX_SLL)
ret_val = call_functor<Tins::SLL>(data, packet, header);
else if(data->iface_type == DLT_PPI)
ret_val = call_functor<Tins::PPI>(data, packet, header);
}
catch(malformed_packet&) {
ret_val = true;
}
catch(pdu_not_found&) {
ret_val = true;
}
}
template<class T>
class HandlerProxy {
@@ -413,7 +335,7 @@ namespace Tins {
/**
* Dereferences the iterator.
* \return references to the current packet.
* \return reference to the current packet.
*/
PDU &operator*() {
return *pkt.pdu();
@@ -431,7 +353,7 @@ namespace Tins {
* Compares this iterator for equality.
* \param rhs The iterator to be compared to.
*/
bool operator==(const SnifferIterator &rhs) {
bool operator==(const SnifferIterator &rhs) const {
return sniffer == rhs.sniffer;
}
@@ -439,7 +361,7 @@ namespace Tins {
* Compares this iterator for in-equality.
* \param rhs The iterator to be compared to.
*/
bool operator!=(const SnifferIterator &rhs) {
bool operator!=(const SnifferIterator &rhs) const {
return !(*this == rhs);
}
private:
@@ -452,6 +374,21 @@ namespace Tins {
BaseSniffer *sniffer;
Packet pkt;
};
template<class Functor>
void Tins::BaseSniffer::sniff_loop(Functor function, uint32_t max_packets) {
for(iterator it = begin(); it != end(); ++it) {
try {
// If the functor returns false, we're done
if(!function(*it))
return;
}
catch(malformed_packet&) { }
catch(pdu_not_found&) { }
if(max_packets && --max_packets == 0)
return;
}
}
}
#endif // TINS_SNIFFER_H

View File

@@ -35,14 +35,13 @@ using std::string;
using std::runtime_error;
namespace Tins {
BaseSniffer::BaseSniffer() : handle(0), mask(0)
BaseSniffer::BaseSniffer()
: handle(0), mask(0)
{
actual_filter.bf_insns = 0;
}
BaseSniffer::~BaseSniffer() {
if(actual_filter.bf_insns)
pcap_freecode(&actual_filter);
if(handle)
pcap_close(handle);
}
@@ -53,21 +52,16 @@ void BaseSniffer::init(pcap_t *phandle, const std::string &filter,
handle = phandle;
mask = if_mask;
iface_type = pcap_datalink(handle);
actual_filter.bf_insns = 0;
if(!filter.empty() && !set_filter(filter))
throw runtime_error("Invalid filter");
}
bool BaseSniffer::compile_set_filter(const string &filter, bpf_program &prog) {
return (pcap_compile(handle, &prog, filter.c_str(), 0, mask) != -1 && pcap_setfilter(handle, &prog) != -1);
}
PtrPacket BaseSniffer::next_packet() {
pcap_pkthdr header;
PDU *ret = 0;
while(!ret) {
const u_char *content = pcap_next(handle, &header);
const int iface_type = pcap_datalink(handle);
if(content) {
try {
if(iface_type == DLT_EN10MB) {
@@ -105,6 +99,10 @@ int BaseSniffer::get_fd() {
return pcap_get_selectable_fd(handle);
}
int BaseSniffer::link_type() const {
return pcap_datalink(handle);
}
BaseSniffer::iterator BaseSniffer::begin() {
return iterator(this);
}
@@ -114,17 +112,12 @@ BaseSniffer::iterator BaseSniffer::end() {
}
bool BaseSniffer::set_filter(const std::string &filter) {
if(actual_filter.bf_insns)
pcap_freecode(&actual_filter);
return compile_set_filter(filter, actual_filter);
}
int BaseSniffer::proxy_pcap_loop(pcap_t *p1, int p2, pcap_handler p3, u_char *p4) {
return pcap_loop(p1, p2, p3, p4);
}
void BaseSniffer::PCapLoopBreaker::proxy_pcap_breakloop(pcap_t *p1) {
pcap_breakloop(p1);
bpf_program prog;
if(pcap_compile(handle, &prog, filter.c_str(), 0, mask) == -1)
return false;
bool result = pcap_setfilter(handle, &prog) != -1;
pcap_freecode(&prog);
return result;
}
// ****************************** Sniffer ******************************