From 7bc1ab41f71b0e3d19c4c37ca66086fc488ad29a Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Tue, 22 Mar 2016 19:49:26 -0700 Subject: [PATCH] Add TCP and Utils::resolve_hwaddress active tests [ci skip] --- tests/active_tests/include/active_test.h | 10 ++- tests/active_tests/include/configuration.h | 10 +++ tests/active_tests/include/ipv4_tests.h | 1 - tests/active_tests/include/tcp_tests.h | 52 +++++++++++ tests/active_tests/include/utils_tests.h | 52 +++++++++++ tests/active_tests/src/active_test.cpp | 40 ++++++++- tests/active_tests/src/active_test_runner.cpp | 20 +---- tests/active_tests/src/configuration.cpp | 17 ++++ tests/active_tests/src/main.cpp | 4 + tests/active_tests/src/packet_capturer.cpp | 7 +- tests/active_tests/src/tcp_tests.cpp | 87 ++++++++++++++++++ tests/active_tests/src/utils_test.cpp | 90 +++++++++++++++++++ 12 files changed, 366 insertions(+), 24 deletions(-) create mode 100644 tests/active_tests/include/tcp_tests.h create mode 100644 tests/active_tests/include/utils_tests.h create mode 100644 tests/active_tests/src/tcp_tests.cpp create mode 100644 tests/active_tests/src/utils_test.cpp diff --git a/tests/active_tests/include/active_test.h b/tests/active_tests/include/active_test.h index b5753fe..3c7d265 100644 --- a/tests/active_tests/include/active_test.h +++ b/tests/active_tests/include/active_test.h @@ -34,6 +34,7 @@ #include #include "tins/packet_sender.h" #include "configuration.h" +#include "packet_capturer.h" namespace Tins { @@ -59,17 +60,22 @@ public: virtual ~ActiveTest() = default; void execute(); - virtual std::string name() const = 0; + std::string log_prefix() const; bool matches_packet(const Tins::PDU& pdu) const; - virtual void validate_packet(const Tins::PDU& pdu) = 0; + bool is_enabled() const; + void validate(PacketCapturer::PacketStorage& packets); + virtual std::string name() const = 0; protected: const PacketSenderPtr& packet_sender() const; const ConfigurationPtr& configuration() const; virtual bool test_matches_packet(const Tins::PDU& pdu) const = 0; virtual void execute_test() = 0; + virtual void validate_packet(const Tins::PDU& pdu) = 0; + void disable_on_platform(Configuration::Platform platform); private: PacketSenderPtr packet_sender_; ConfigurationPtr configuration_; + unsigned disabled_platforms_ = 0; }; #endif // TINS_ACTIVE_TEST_H diff --git a/tests/active_tests/include/configuration.h b/tests/active_tests/include/configuration.h index 0452236..deaa111 100644 --- a/tests/active_tests/include/configuration.h +++ b/tests/active_tests/include/configuration.h @@ -36,6 +36,14 @@ class Configuration { public: + enum Platform { + WINDOWS = 1, + BSD = 2, + LINUX = 4 + }; + + Configuration(); + void interface(const Tins::NetworkInterface& interface); void source_port(uint16_t value); void destination_port(uint16_t value); @@ -43,10 +51,12 @@ public: const Tins::NetworkInterface& interface() const; uint16_t source_port() const; uint16_t destination_port() const; + Platform current_platform() const; private: Tins::NetworkInterface interface_; uint16_t source_port_ = 0; uint16_t destination_port_ = 0; + Platform current_platform_; }; #endif // TINS_ACTIVE_TEST_CONFIGURATION_H diff --git a/tests/active_tests/include/ipv4_tests.h b/tests/active_tests/include/ipv4_tests.h index bc0029d..5eab0cd 100644 --- a/tests/active_tests/include/ipv4_tests.h +++ b/tests/active_tests/include/ipv4_tests.h @@ -30,7 +30,6 @@ #ifndef TINS_IPV4_TESTS_H #define TINS_IPV4_TESTS_H -#include #include #include "active_test.h" diff --git a/tests/active_tests/include/tcp_tests.h b/tests/active_tests/include/tcp_tests.h new file mode 100644 index 0000000..fa4858a --- /dev/null +++ b/tests/active_tests/include/tcp_tests.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, 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_ACTIVE_TCP_TESTS_H +#define TINS_ACTIVE_TCP_TESTS_H + +#include +#include "active_test.h" +#include "tins/ip_address.h" + +class TCPSynTest : public ActiveTest { +public: + TCPSynTest(const PacketSenderPtr& packet_sender, + const ConfigurationPtr& configuration); + + std::string name() const; +private: + void execute_test(); + void validate_packet(const Tins::PDU& pdu); + bool test_matches_packet(const Tins::PDU& pdu) const; + + Tins::IPv4Address target_address_; + uint32_t sequence_number_; +}; + +#endif // TINS_ACTIVE_TCP_TESTS_H diff --git a/tests/active_tests/include/utils_tests.h b/tests/active_tests/include/utils_tests.h new file mode 100644 index 0000000..9f517af --- /dev/null +++ b/tests/active_tests/include/utils_tests.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016, 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_UTILS_ACTIVE_TEST_H +#define TINS_UTILS_ACTIVE_TEST_H + +#include "tins/ip_address.h" +#include "tins/hw_address.h" +#include "active_test.h" + +class ResolveHWAddressTest : public ActiveTest { +public: + ResolveHWAddressTest(const PacketSenderPtr& packet_sender, + const ConfigurationPtr& configuration); + + std::string name() const; +private: + bool test_matches_packet(const Tins::PDU& pdu) const; + void execute_test(); + void validate_packet(const Tins::PDU& pdu); + + Tins::IPv4Address target_address_; + Tins::HWAddress<6> resolved_address_; +}; + +#endif // TINS_UTILS_ACTIVE_TEST_H diff --git a/tests/active_tests/src/active_test.cpp b/tests/active_tests/src/active_test.cpp index 628a89d..6084102 100644 --- a/tests/active_tests/src/active_test.cpp +++ b/tests/active_tests/src/active_test.cpp @@ -34,6 +34,7 @@ using std::cout; using std::endl; +using std::string; using Tins::PDU; using Tins::pdu_not_found; @@ -46,7 +47,16 @@ ActiveTest::ActiveTest(const PacketSenderPtr& packet_sender, } void ActiveTest::execute() { - execute_test(); + if (is_enabled()) { + execute_test(); + } + else { + cout << log_prefix() << "not running as test is disabled on this platform" << endl; + } +} + +string ActiveTest::log_prefix() const { + return "[" + name() + "] "; } bool ActiveTest::matches_packet(const PDU& pdu) const { @@ -61,6 +71,31 @@ bool ActiveTest::matches_packet(const PDU& pdu) const { } } +bool ActiveTest::is_enabled() const { + return (configuration_->current_platform() & disabled_platforms_) == 0; +} + +void ActiveTest::validate(PacketCapturer::PacketStorage& packets) { + string prefix = log_prefix(); + size_t i = 0; + while (i < packets.size() && !matches_packet(*packets[i])) { + ++i; + } + if (i == packets.size()) { + cout << prefix << "ERROR: Packet was not captured" << endl; + } + else { + try { + validate_packet(*packets[i]); + cout << prefix << "OK" << endl; + } + catch (TestFailed& ex) { + cout << prefix << "ERROR: " << ex.what() << endl; + } + packets.erase(packets.begin() + i); + } +} + const ActiveTest::PacketSenderPtr& ActiveTest::packet_sender() const { return packet_sender_; } @@ -69,3 +104,6 @@ const ActiveTest::ConfigurationPtr& ActiveTest::configuration() const { return configuration_; } +void ActiveTest::disable_on_platform(Configuration::Platform platform) { + disabled_platforms_ |= static_cast(platform); +} diff --git a/tests/active_tests/src/active_test_runner.cpp b/tests/active_tests/src/active_test_runner.cpp index 5fde4ec..b0f84f4 100644 --- a/tests/active_tests/src/active_test_runner.cpp +++ b/tests/active_tests/src/active_test_runner.cpp @@ -90,23 +90,9 @@ void ActiveTestRunner::do_run() { auto packets = capturer_.captured_packets(); cout << prefix << "Captured " << packets.size() << " packets" << endl; for (const auto& test : tests_) { - prefix = "[" + test->name() + "] "; - size_t i = 0; - while (i < packets.size() && !test->matches_packet(*packets[i])) { - ++i; - } - if (i == packets.size()) { - cout << prefix << "ERROR: Packet was not captured" << endl; - } - else { - try { - test->validate_packet(*packets[i]); - cout << prefix << "OK" << endl; - } - catch (TestFailed& ex) { - cout << prefix << "ERROR: " << ex.what() << endl; - } - packets.erase(packets.begin() + i); + if (test->is_enabled()) { + test->validate(packets); } + } } diff --git a/tests/active_tests/src/configuration.cpp b/tests/active_tests/src/configuration.cpp index c57f87d..9fb94d9 100644 --- a/tests/active_tests/src/configuration.cpp +++ b/tests/active_tests/src/configuration.cpp @@ -27,12 +27,26 @@ * */ +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + #include +#endif + #include "configuration.h" using std::string; using Tins::NetworkInterface; +Configuration::Configuration() { + #ifdef _WIN32 + current_platform_ = WINDOWS; + #elif defined(BSD) || defined(__FreeBSD_kernel__) + current_platform_ = BSD; + #else + current_platform_ = LINUX; + #endif +} + void Configuration::interface(const NetworkInterface& interface) { interface_ = interface; } @@ -57,3 +71,6 @@ uint16_t Configuration::destination_port() const { return destination_port_; } +Configuration::Platform Configuration::current_platform() const { + return current_platform_; +} diff --git a/tests/active_tests/src/main.cpp b/tests/active_tests/src/main.cpp index f18ba39..380df16 100644 --- a/tests/active_tests/src/main.cpp +++ b/tests/active_tests/src/main.cpp @@ -32,6 +32,8 @@ #include "configuration.h" #include "active_test_runner.h" #include "ipv4_tests.h" +#include "tcp_tests.h" +#include "utils_tests.h" #include "tins/network_interface.h" using std::cerr; @@ -51,6 +53,8 @@ int main() { ActiveTestRunner runner(config); runner.add_test(); runner.add_test(); + runner.add_test(); + runner.add_test(); if (!runner.validate_tests()) { throw runtime_error("Test validation failed"); diff --git a/tests/active_tests/src/packet_capturer.cpp b/tests/active_tests/src/packet_capturer.cpp index 0045e8a..1d09abb 100644 --- a/tests/active_tests/src/packet_capturer.cpp +++ b/tests/active_tests/src/packet_capturer.cpp @@ -95,10 +95,11 @@ bool PacketCapturer::callback(const PDU& pdu) { string PacketCapturer::make_filter(const Configuration& configuration) const { ostringstream oss; - oss << "((tcp or udp) and src port " << configuration.source_port() - << " and dst port " << configuration.destination_port() << ") or icmp" + oss << "((tcp or udp) and (port " << configuration.source_port() + << " or port " << configuration.destination_port() << ")) or icmp" // Fragmentted IP packets - << " or (ip[6:2] & 0x1fff) > 0"; + << " or (ip[6:2] & 0x1fff) > 0" + << " or arp"; return oss.str(); } diff --git a/tests/active_tests/src/tcp_tests.cpp b/tests/active_tests/src/tcp_tests.cpp new file mode 100644 index 0000000..4fcfbd7 --- /dev/null +++ b/tests/active_tests/src/tcp_tests.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016, 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 +#include "tcp_tests.h" +#include "tins/tcp.h" +#include "tins/ip.h" +#include "tins/utils.h" + +using std::string; +using std::cout; +using std::endl; +using std::random_device; + +using Tins::PDU; +using Tins::TCP; +using Tins::IP; +using Tins::Utils::resolve_domain; + +TCPSynTest::TCPSynTest(const PacketSenderPtr& packet_sender, + const ConfigurationPtr& configuration) +: ActiveTest(packet_sender, configuration) { + disable_on_platform(Configuration::WINDOWS); +} + +string TCPSynTest::name() const { + return "tcp_syn_test"; +} + +void TCPSynTest::execute_test() { + random_device rnd; + target_address_ = resolve_domain("www.example.com"); + sequence_number_ = static_cast(rnd()); + cout << log_prefix() << "Resolved target address to " << target_address_ << endl; + + auto packet = IP(target_address_) / TCP(80, configuration()->source_port()); + TCP& tcp = packet.rfind_pdu(); + tcp.seq(sequence_number_); + tcp.flags(TCP::SYN); + packet_sender()->send(packet); +} + +void TCPSynTest::validate_packet(const PDU& pdu) { + const TCP& tcp = pdu.rfind_pdu(); + if (tcp.flags() != (TCP::SYN | TCP::ACK) && tcp.flags() != TCP::RST) { + throw TestFailed("Invalid flags received"); + } +} + +bool TCPSynTest::test_matches_packet(const PDU& pdu) const { + if (pdu.rfind_pdu().src_addr() != target_address_) { + return false; + } + const TCP& tcp = pdu.rfind_pdu(); + if (tcp.sport() != 80) { + return false; + } + return tcp.ack_seq() == sequence_number_ + 1; + +} diff --git a/tests/active_tests/src/utils_test.cpp b/tests/active_tests/src/utils_test.cpp new file mode 100644 index 0000000..77d3199 --- /dev/null +++ b/tests/active_tests/src/utils_test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016, 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 +#include "utils_tests.h" +#include "tins/ethernetII.h" +#include "tins/arp.h" +#include "tins/utils.h" + +using std::cout; +using std::endl; +using std::string; +using std::vector; +using std::ostringstream; + +using Tins::PDU; +using Tins::EthernetII; +using Tins::ARP; +using Tins::IPv4Address; +using Tins::HWAddress; +using Tins::Utils::RouteEntry; + +ResolveHWAddressTest::ResolveHWAddressTest(const PacketSenderPtr& packet_sender, + const ConfigurationPtr& configuration) +: ActiveTest(packet_sender, configuration) { + vector entries = Tins::Utils::route_entries(); + string interface_name = configuration->interface().name(); + for (const auto& entry : entries) { + if (entry.interface == interface_name && entry.gateway != "0.0.0.0") { + target_address_ = entry.gateway; + } + } + disable_on_platform(Configuration::WINDOWS); +} + +string ResolveHWAddressTest::name() const { + return "resolve_hwaddress"; +} + +bool ResolveHWAddressTest::test_matches_packet(const PDU& pdu) const { + const ARP& arp = pdu.rfind_pdu(); + return arp.opcode() == ARP::REPLY && arp.sender_ip_addr() == target_address_; +} + +void ResolveHWAddressTest::execute_test() { + cout << log_prefix() << "trying to resolve " << target_address_ << endl; + resolved_address_ = Tins::Utils::resolve_hwaddr(configuration()->interface(), + target_address_, + *packet_sender()); + auto local_ip_address = configuration()->interface().ipv4_address(); + auto local_hw_address = configuration()->interface().hw_address(); + auto packet = ARP::make_arp_request(target_address_, local_ip_address, local_hw_address); + packet_sender()->send(packet); +} + +void ResolveHWAddressTest::validate_packet(const PDU& pdu) { + const ARP& arp = pdu.rfind_pdu(); + if (arp.sender_hw_addr() != resolved_address_) { + ostringstream oss; + oss << "Expected address " << resolved_address_ << " but got " << arp.sender_hw_addr(); + throw TestFailed(oss.str()); + } +}