diff --git a/include/tins/sniffer.h b/include/tins/sniffer.h index 942cd71..cf9cae6 100644 --- a/include/tins/sniffer.h +++ b/include/tins/sniffer.h @@ -45,6 +45,7 @@ #include + namespace Tins { class SnifferIterator; class SnifferConfiguration; @@ -72,7 +73,8 @@ public: * This constructor is available only in C++11. */ BaseSniffer(BaseSniffer &&rhs) TINS_NOEXCEPT - : handle_(0), mask_(), extract_raw_(false) { + : handle_(0), mask_(), extract_raw_(false), + pcap_sniffing_method_(pcap_loop) { *this = std::move(rhs); } @@ -85,6 +87,7 @@ public: swap(handle_, rhs.handle_); swap(mask_, rhs.mask_); swap(extract_raw_, rhs.extract_raw_); + swap(pcap_sniffing_method_, rhs.pcap_sniffing_method_); return* this; } #endif @@ -237,6 +240,35 @@ public: */ void set_extract_raw_pdus(bool value); + /** + * \brief function pointer for the sniffing method + * + * By default, libtins uses `pcap_loop` to sniff packets. With + * `set_pcap_sniffing_method` it is possible to specify an alternative + * sniffing method, for example `pcap_dispatch`, or a custom function. + * This function pointer has the same interface as `pcap_loop` and + * `pcap_dispatch`. + * + * \sa set_pcap_sniffing_method + */ + typedef int(*PcapSniffingMethod)(pcap_t*, int, pcap_handler, u_char*); + + /** + * \brief set sniffing method to either pcap_loop or pcap_dispatch. + * + * By default, packets are sniffed with `pcap_loop`, which only returns if + * a packet is received, thus ignoring timeout expiration, if any is set. + * With this method it is possible to pass an alternative sniffer function, + * e.g. `pcap_dispatch`, that honors timeouts, or a custom function with + * the same signature. + * + * See the relevant manual pages for pcap_loop and pcap_dispatch for more + * information on their behavior + * + * \sa PcapSniffingMethod + */ + void set_pcap_sniffing_method(PcapSniffingMethod method); + /** * \brief Retrieves this sniffer's link type. * @@ -282,6 +314,7 @@ private: pcap_t* handle_; bpf_u_int32 mask_; bool extract_raw_; + PcapSniffingMethod pcap_sniffing_method_; }; /** @@ -567,6 +600,12 @@ public: */ void set_filter(const std::string& filter); + /** + * Sets the pcap sniffing method to use. + * \param method The sniffing method to be used. + */ + void set_pcap_sniffing_method(BaseSniffer::PcapSniffingMethod method); + /** * Sets the rfmon option. * \param enabled The rfmon option value. @@ -608,6 +647,7 @@ protected: IMMEDIATE_MODE = 16, DIRECTION = 32, TIMESTAMP_PRECISION = 64, + PCAP_SNIFFING_METHOD = 128, }; void configure_sniffer_pre_activation(Sniffer& sniffer) const; @@ -619,6 +659,7 @@ protected: unsigned snap_len_; unsigned buffer_size_; std::string filter_; + BaseSniffer::PcapSniffingMethod pcap_sniffing_method_; unsigned timeout_; bool promisc_; bool rfmon_; diff --git a/src/sniffer.cpp b/src/sniffer.cpp index 87a1141..dc97feb 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -212,7 +212,7 @@ PtrPacket BaseSniffer::next_packet() { // keep calling pcap_loop until a well-formed packet is found. while (data.pdu == 0 && data.packet_processed) { data.packet_processed = false; - if (pcap_loop(handle_, 1, handler, (u_char*)&data) < 0) { + if (pcap_sniffing_method_(handle_, 1, handler, (u_char*)&data) < 0) { return PtrPacket(0, Timestamp()); } } @@ -223,6 +223,13 @@ void BaseSniffer::set_extract_raw_pdus(bool value) { extract_raw_ = value; } +void BaseSniffer::set_pcap_sniffing_method(PcapSniffingMethod method) { + if (method == nullptr) { + throw std::runtime_error("Sniffing method cannot be null"); + } + pcap_sniffing_method_ = method; +} + void BaseSniffer::stop_sniff() { pcap_breakloop(handle_); } @@ -419,8 +426,9 @@ const unsigned SnifferConfiguration::DEFAULT_SNAP_LEN = 65535; const unsigned SnifferConfiguration::DEFAULT_TIMEOUT = 1000; SnifferConfiguration::SnifferConfiguration() -: flags_(0), snap_len_(DEFAULT_SNAP_LEN), buffer_size_(0), timeout_(DEFAULT_TIMEOUT), - promisc_(false), rfmon_(false), immediate_mode_(false), direction_(PCAP_D_INOUT), +: flags_(0), snap_len_(DEFAULT_SNAP_LEN), buffer_size_(0), + pcap_sniffing_method_(pcap_loop), timeout_(DEFAULT_TIMEOUT), promisc_(false), + rfmon_(false), immediate_mode_(false), direction_(PCAP_D_INOUT), timestamp_precision_(0) { } @@ -428,6 +436,7 @@ SnifferConfiguration::SnifferConfiguration() void SnifferConfiguration::configure_sniffer_pre_activation(Sniffer& sniffer) const { sniffer.set_snap_len(snap_len_); sniffer.set_timeout(timeout_); + sniffer.set_pcap_sniffing_method(pcap_sniffing_method_); if ((flags_ & BUFFER_SIZE) != 0) { sniffer.set_buffer_size(buffer_size_); } @@ -488,6 +497,11 @@ void SnifferConfiguration::set_filter(const string& filter) { filter_ = filter; } +void SnifferConfiguration::set_pcap_sniffing_method(BaseSniffer::PcapSniffingMethod method) { + flags_ |= PCAP_SNIFFING_METHOD; + pcap_sniffing_method_ = method; +} + void SnifferConfiguration::set_rfmon(bool enabled) { flags_ |= RFMON; rfmon_ = enabled;