From a7a4105cf8c5447770a98351c2b652a2c3364b5c Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sat, 30 Aug 2014 23:01:46 -0300 Subject: [PATCH] Added OfflinePacketFilter class. --- include/data_link_type.h | 3 + include/offline_packet_filter.h | 133 ++++++++++++++++++++++++++++ src/CMakeLists.txt | 1 + src/offline_packet_filter.cpp | 86 ++++++++++++++++++ tests/src/CMakeLists.txt | 3 + tests/src/offline_packet_filter.cpp | 77 ++++++++++++++++ 6 files changed, 303 insertions(+) create mode 100644 include/offline_packet_filter.h create mode 100644 src/offline_packet_filter.cpp create mode 100644 tests/src/offline_packet_filter.cpp diff --git a/include/data_link_type.h b/include/data_link_type.h index 6d05377..9045031 100644 --- a/include/data_link_type.h +++ b/include/data_link_type.h @@ -44,6 +44,9 @@ class PPI; /** * \brief Maps a libtins link layer PDU to a libpcap data link identifier. + * + * This should be instantiated with any object that represents a + * link layer PDU (EthernetII, Dot11, RadioTap, etc) */ template struct DataLinkType; diff --git a/include/offline_packet_filter.h b/include/offline_packet_filter.h new file mode 100644 index 0000000..8a747f3 --- /dev/null +++ b/include/offline_packet_filter.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TINS_OFFLINE_PACKET_FILTER_H +#define TINS_OFFLINE_PACKET_FILTER_H + +#include +#include +#include "data_link_type.h" + +namespace Tins { +class PDU; + +/** + * \class OfflinePacketFilter + * + * \brief Wraps a pcap filter and matches it against a packet or buffer. + * + * This is a thin wrapper over pcap_offline_filter. You can use + * it to perform packet filtering outside of Sniffer instances. + * + * A potential use case would be if you are capturing packets that are + * sent from another host over UDP. You would recieve UDP packets, then + * parse their content, and apply the OfflinePacketFilter over the + * wrapped packet. + */ +class OfflinePacketFilter { +public: + /** + * Constructs an OfflinePacketFilter object. + * + * \param filter The pcap filter to use. + * \param lt The link layer type to use. + * \param snap_len The snapshot length to use. + */ + template + OfflinePacketFilter(const std::string& filter, const DataLinkType& lt, + unsigned int snap_len = 65535) + : string_filter(filter) + { + init(filter, lt.get_type(), snap_len); + } + + /** + * \brief Copy constructor. + * + * Note that during copy construction the pcap filter is + * recompiled. Therefore, it might be somehow expensive to + * copy OfflinePacketFilters. + * + * \param other The filter to be copied. + */ + OfflinePacketFilter(const OfflinePacketFilter& other); + + /** + * \brief Copy assignment operator. + * + * \param other The filter to be copied. + * + * \sa OfflinePacketFilter + */ + OfflinePacketFilter& operator=(const OfflinePacketFilter& other); + + /** + * Releases the compiled pcap filter and handle. + */ + ~OfflinePacketFilter(); + + /** + * \brief Applies the compiled filter on the provided buffer. + * + * This method uses pcap_offline_filter on the provided buffer + * and returns a bool indicating if the packet pointed by the buffer + * matches the filter. + * + * \param buffer A pointer to a buffer which holds a raw packet. + * \param total_sz The length of the buffer pointed by buffer. + * \return true iff the packet matches the filter. + */ + bool matches_filter(const uint8_t* buffer, uint32_t total_sz) const; + + /** + * \brief Applies the compiled filter on the provided packet. + * + * This method checks whether the provided packet matches the filter. + * Since this uses pcap filters and they work over a raw data buffer, + * this method serialices the packet and then applies the filter. + * Therefore, this can be quite expensive to use. If you have access + * to the packet before constructing a PDU from it, it is recommended + * to use the other overload over the raw buffer. + * + * \param pdu The packet to be matched against the filter. + * \return true iff the packet matches the filter. + */ + bool matches_filter(PDU& pdu) const; +private: + void init(const std::string& pcap_filter, int link_type, + unsigned int snap_len); + + + pcap_t* handle; + mutable bpf_program filter; + std::string string_filter; +}; +} // Tins + +#endif // TINS_OFFLINE_PACKET_FILTER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b74c59a..c1266c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,7 @@ ADD_LIBRARY( llc.cpp loopback.cpp network_interface.cpp + offline_packet_filter.cpp packet_sender.cpp packet_writer.cpp ppi.cpp diff --git a/src/offline_packet_filter.cpp b/src/offline_packet_filter.cpp new file mode 100644 index 0000000..2adf4ff --- /dev/null +++ b/src/offline_packet_filter.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include "offline_packet_filter.h" +#include "pdu.h" + +namespace Tins { + +OfflinePacketFilter::OfflinePacketFilter(const OfflinePacketFilter& other) +{ + *this = other; +} + +OfflinePacketFilter& OfflinePacketFilter::operator=(const OfflinePacketFilter& other) +{ + string_filter = other.string_filter; + init(string_filter, pcap_datalink(other.handle), pcap_snapshot(other.handle)); + return *this; +} + +OfflinePacketFilter::~OfflinePacketFilter() +{ + pcap_freecode(&filter); + pcap_close(handle); +} + +void OfflinePacketFilter::init(const std::string& pcap_filter, int link_type, + unsigned int snap_len) +{ + handle = pcap_open_dead( + link_type, + snap_len + ); + if(pcap_compile(handle, &filter, pcap_filter.c_str(), 1, 0xffffffff) == -1) + { + throw std::runtime_error(pcap_geterr(handle)); + } +} + +bool OfflinePacketFilter::matches_filter(const uint8_t* buffer, + uint32_t total_sz) const +{ + pcap_pkthdr header = {}; + header.len = total_sz; + header.caplen = total_sz; + return pcap_offline_filter( + &filter, + &header, + buffer + ); +} + +bool OfflinePacketFilter::matches_filter(PDU& pdu) const +{ + PDU::serialization_type buffer = pdu.serialize(); + return matches_filter(&buffer[0], buffer.size()); +} + +} // Tins diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index fee1f69..75d0d95 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -54,6 +54,7 @@ ADD_CUSTOM_TARGET( LLCTest MatchesResponseTest NetworkInterfaceTest + OfflinePacketFilterTest PDUTest PPITest PPPoETest @@ -93,6 +94,7 @@ ADD_EXECUTABLE(IPv6AddressTest EXCLUDE_FROM_ALL ipv6address.cpp) ADD_EXECUTABLE(LLCTest EXCLUDE_FROM_ALL llc.cpp) ADD_EXECUTABLE(MatchesResponseTest EXCLUDE_FROM_ALL matches_response.cpp) ADD_EXECUTABLE(NetworkInterfaceTest EXCLUDE_FROM_ALL network_interface.cpp) +ADD_EXECUTABLE(OfflinePacketFilterTest EXCLUDE_FROM_ALL offline_packet_filter.cpp) ADD_EXECUTABLE(PDUTest EXCLUDE_FROM_ALL pdu.cpp) ADD_EXECUTABLE(PPITest EXCLUDE_FROM_ALL ppi.cpp) ADD_EXECUTABLE(PPPoETest EXCLUDE_FROM_ALL pppoe.cpp) @@ -170,6 +172,7 @@ ADD_TEST(IPv6Address IPv6AddressTest) ADD_TEST(LLC LLCTest) ADD_TEST(MatchesResponse MatchesResponseTest) ADD_TEST(NetworkInterface NetworkInterfaceTest) +ADD_TEST(OfflinePacketFilter OfflinePacketFilterTest) ADD_TEST(PDU PDUTest) ADD_TEST(PPI PPITest) ADD_TEST(PPPoE PPPoETest) diff --git a/tests/src/offline_packet_filter.cpp b/tests/src/offline_packet_filter.cpp new file mode 100644 index 0000000..25c6938 --- /dev/null +++ b/tests/src/offline_packet_filter.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include "offline_packet_filter.h" +#include "ip.h" +#include "tcp.h" +#include "ethernetII.h" +#include "dot3.h" +#include "sll.h" +#include "llc.h" +#include "udp.h" +#include "rawpdu.h" + +using namespace Tins; + +class OfflinePacketFilterTest : public testing::Test { +public: + +}; + +TEST_F(OfflinePacketFilterTest, CopyConstructor) { + OfflinePacketFilter filter1("udp and port 111", DataLinkType()); + OfflinePacketFilter filter2(filter1); + OfflinePacketFilter filter3("tcp", DataLinkType()); + filter3 = filter1; + + { + EthernetII pkt = EthernetII() / IP() / UDP(111, 11) / RawPDU("test"); + EXPECT_TRUE(filter1.matches_filter(pkt)); + EXPECT_TRUE(filter2.matches_filter(pkt)); + EXPECT_TRUE(filter3.matches_filter(pkt)); + } + + { + EthernetII pkt = EthernetII() / IP() / TCP(111, 11) / RawPDU("test"); + EXPECT_FALSE(filter1.matches_filter(pkt)); + EXPECT_FALSE(filter2.matches_filter(pkt)); + EXPECT_FALSE(filter3.matches_filter(pkt)); + } +} + +TEST_F(OfflinePacketFilterTest, MatchesFilterEthTcp) { + OfflinePacketFilter filter("ip and port 55", DataLinkType()); + { + EthernetII pkt = EthernetII() / IP() / TCP(55, 11) / RawPDU("test"); + EXPECT_TRUE(filter.matches_filter(pkt)); + } + { + EthernetII pkt = EthernetII() / IP() / TCP(45, 11) / RawPDU("test"); + EXPECT_FALSE(filter.matches_filter(pkt)); + } +} + +TEST_F(OfflinePacketFilterTest, MatchesFilterEth) { + OfflinePacketFilter filter("ether dst 00:01:02:03:04:05", DataLinkType()); + { + EthernetII pkt = EthernetII("00:01:02:03:04:05") / IP() / TCP(55, 11) / RawPDU("test"); + EXPECT_TRUE(filter.matches_filter(pkt)); + } + { + EthernetII pkt = EthernetII() / IP() / TCP(45, 11) / RawPDU("test"); + EXPECT_FALSE(filter.matches_filter(pkt)); + } +} + +TEST_F(OfflinePacketFilterTest, MatchesFilterSLLTcp) { + OfflinePacketFilter filter("ip and port 55", DataLinkType()); + { + SLL pkt = SLL() / IP() / TCP(55, 11) / RawPDU("test"); + EXPECT_TRUE(filter.matches_filter(pkt)); + } + { + SLL pkt = SLL() / IP() / TCP(45, 11) / RawPDU("test"); + EXPECT_FALSE(filter.matches_filter(pkt)); + } +}