diff --git a/examples/traceroute.cpp b/examples/traceroute.cpp index 12fbc12..6fb5bb3 100644 --- a/examples/traceroute.cpp +++ b/examples/traceroute.cpp @@ -31,8 +31,11 @@ #include #include #include +#include #include +#include #include +#include #include #include @@ -43,7 +46,9 @@ public: typedef std::map result_type; Traceroute(NetworkInterface interface, IPv4Address address) - : iface(interface), addr(address) { } + : iface(interface), addr(address), lowest_dest_ttl(std::numeric_limits::max()) { + sequence = std::random_device()(); + } result_type trace() { SnifferConfiguration config; @@ -70,6 +75,10 @@ public: ); send_packets(sender); sniff_thread.join(); + // If the final hop responded, add its address at the appropriate ttl + if (lowest_dest_ttl != std::numeric_limits::max()) { + results[lowest_dest_ttl] = addr; + } // Clear our results and return what we've found return std::move(results); } @@ -79,11 +88,13 @@ private: void send_packets(PacketSender &sender) { // ICMPs are icmp-requests by default IP ip = IP(addr, iface.addresses().ip_addr) / ICMP(); - // We'll find at most 10 hops. + ICMP& icmp = ip.rfind_pdu(); + icmp.sequence(sequence); + // We'll find at most 20 hops. - for(auto i = 1; i <= 10; ++i) { - // Set this "unique" id - ip.id(i); + for(auto i = 1; i <= 20; ++i) { + // Set this ICMP id + icmp.id(i); // Set the time-to-live option ip.ttl(i); @@ -102,20 +113,37 @@ private: } bool sniff_callback(PDU &pdu) { + // Find IP and ICMP PDUs const IP &ip = pdu.rfind_pdu(); - ttl_map::const_iterator iter; - // Fetch the IP PDU attached to the ICMP response - const IP inner_ip = pdu.rfind_pdu().to(); - // Critical section - { - std::lock_guard _(lock); - iter = ttls.find(inner_ip.id()); - } + const ICMP &icmp = pdu.rfind_pdu(); + // Check if this is an ICMP TTL exceeded error response + if (icmp.type() == ICMP::TIME_EXCEEDED) { + // Fetch the IP PDU attached to the ICMP response + const IP inner_ip = pdu.rfind_pdu().to(); + // Now get the ICMP layer + const ICMP& inner_icmp = inner_ip.rfind_pdu(); + // Make sure this is one of our packets. + if (inner_icmp.sequence() == sequence) { + ttl_map::const_iterator iter; - // It's an actual response - if(iter != ttls.end()) { - // Store it - results[inner_ip.id()] = ip.src_addr(); + // Critical section + { + std::lock_guard _(lock); + iter = ttls.find(inner_icmp.id()); + } + + // It's an actual response + if(iter != ttls.end()) { + // Store it + results[inner_icmp.id()] = ip.src_addr(); + } + } + } + // Otherwise, this could be the final hop making an echo response + else if (icmp.type() == ICMP::ECHO_REPLY && icmp.sequence() == sequence && + ip.src_addr() == addr) { + // Keep the lowest ttl seen for the destination. + lowest_dest_ttl = std::min(lowest_dest_ttl, static_cast(icmp.id())); } return running; } @@ -126,10 +154,12 @@ private: ttl_map ttls; result_type results; std::mutex lock; + uint16_t sequence; + int lowest_dest_ttl; }; int main(int argc, char* argv[]) { - if(argc <= 1 && std::cout << "Usage: " << *argv << " \n") + if(argc <= 1 && std::cout << "Usage: " << *argv << " \n") return 1; try { IPv4Address addr((std::string(argv[1]))); @@ -140,7 +170,7 @@ int main(int argc, char* argv[]) { else { std::cout << "Results: " << std::endl; for(const auto &entry : results) { - std::cout << entry.first << " - " << entry.second << std::endl; + std::cout << std::setw(2) << entry.first << " - " << entry.second << std::endl; } } }