diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5249d45..710e0c6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -18,6 +18,7 @@ IF(libtins_FOUND) wps_detect traceroute interfaces_info + icmp_responses ) ELSE(HAVE_CXX11) MESSAGE(WARNING "Disabling some examples since C++11 support is disabled.") @@ -38,6 +39,7 @@ IF(libtins_FOUND) ADD_EXECUTABLE(dns_spoof EXCLUDE_FROM_ALL dns_spoof.cpp) ADD_EXECUTABLE(wps_detect EXCLUDE_FROM_ALL wps_detect.cpp) ADD_EXECUTABLE(interfaces_info EXCLUDE_FROM_ALL interfaces_info.cpp) + ADD_EXECUTABLE(icmp_responses EXCLUDE_FROM_ALL icmp_responses.cpp) ENDIF(HAVE_CXX11) ADD_EXECUTABLE(beacon_display EXCLUDE_FROM_ALL beacon_display.cpp) diff --git a/examples/icmp_responses.cpp b/examples/icmp_responses.cpp new file mode 100644 index 0000000..76929b0 --- /dev/null +++ b/examples/icmp_responses.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015, 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 +#include +#include + +using namespace std; +using namespace Tins; + +// This class captured packets on an interface, using the specified filter +// and will respond with ICMP error packets whenever a packet is captured. +// The response mechanism is pretty naive as it generates a packet which +// has swapped HW and IP addresses (dst as src, src as dst). +// +// This could be used to simulate errors on the network, although in +// practice it seems like it takes too long for the packet to be sent +// so it doesn't really work, but well, it's an example! +class ICMPResponder { +public: + // Use the given interface and ICMP type/code on responses + ICMPResponder(string iface, int type, int code) + : m_iface(iface), m_sender(iface), m_type(type), m_code(code) { + + } + + // Run using the given filter + void run(const string& filter) { + // Initialize the configuration + SnifferConfiguration config; + // Use promiscuous mode + config.set_promisc_mode(true); + // Use this packet filter + config.set_filter(filter); + + // Now create the Sniffer + Sniffer sniffer(m_iface, config); + if (sniffer.link_type() != DLT_EN10MB) { + throw runtime_error("Ethernet interfaces only supported"); + } + // Start the sniffing! For each packet, ICMPReponder::callback will be called + sniffer.sniff_loop(bind(&ICMPResponder::callback, this, placeholders::_1)); + } +private: + // Extracts the payload to be used over the ICMP layer in the response. + // This will be the entire IP header + 8 bytes of the next header. + RawPDU extract_icmp_payload(IP& pdu) { + PDU::serialization_type buffer = pdu.serialize(); + // Use whole IP + 8 bytes of next header. + size_t end_index = pdu.header_size() + 8; + return RawPDU(buffer.begin(), buffer.begin() + end_index); + } + + // Generates an ICMP response given a packet. + EthernetII generate_response(PDU& pdu) { + // Find Ethernet and IP headers. + EthernetII& received_eth = pdu.rfind_pdu(); + IP& received_ip = pdu.rfind_pdu(); + + // Create an Ethernet response, flipping the addresses + EthernetII output(received_eth.src_addr(), received_eth.dst_addr()); + // Append an IP PDU, again flipping addresses. + output /= IP(received_ip.src_addr(), received_ip.dst_addr()); + + // Now generate the ICMP layer using the type and code provided. + ICMP icmp; + icmp.type(static_cast(m_type)); + icmp.code(m_code); + // Append the ICMP layer to our packet + output /= icmp; + // Extract the payload to be used over ICMP. + output /= extract_icmp_payload(received_ip); + return output; + } + + // Packet capture callback + bool callback(PDU& pdu) { + // Generate a response for this packet + EthernetII response = generate_response(pdu); + // Send this packet! + m_sender.send(response); + return true; + } + + string m_iface; + PacketSender m_sender; + int m_type; + int m_code; +}; + +int main(int argc, char *argv[]) { + const int type = 3; + const int code = 0; + if (argc < 3) { + cout << "Usage: " << argv[0] << " " << endl; + return 1; + } + string iface = argv[1]; + string filter = argv[2]; + try { + ICMPResponder responder(iface, type, code); + responder.run(filter); + } + catch (exception& ex) { + cout << "Error: " << ex.what() << endl; + } +} \ No newline at end of file