Skip to content

Commit

Permalink
Refactored traceroute code into udpv4 probe class
Browse files Browse the repository at this point in the history
  • Loading branch information
insomniacslk committed Sep 25, 2017
1 parent e47892e commit 942a421
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 123 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_library(dublintraceroute SHARED
src/common.cc
src/dublin_traceroute.cc
src/hop.cc
src/udpv4probe.cc
src/traceroute_results.cc
)

Expand Down
49 changes: 49 additions & 0 deletions include/dublintraceroute/udpv4probe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* \file udpv4probe.h
* \Author Andrea Barberio <[email protected]>
* \date 2017
* \brief Definition of the UDPProbev4 class
*
* This file contains the definition of the UDPv4Probe class, which represents
* an UDP probe that will be sent over IPv4.
*
* \sa udpv4probe.cc
*/

#ifndef _UDPV4PROBE_H
#define _UDPV4PROBE_H

#include <tins/tins.h>

using namespace Tins;


class UDPv4Probe {
private:
IPv4Address local_addr_;
IPv4Address remote_addr_;
uint16_t local_port_;
uint16_t remote_port_;
uint8_t ttl_;
public:
const IPv4Address local_addr() const { return local_addr_; }
const IPv4Address remote_addr() const { return remote_addr_; }
const uint16_t local_port() const { return local_port_; };
const uint16_t remote_port() const { return remote_port_; };

UDPv4Probe(
IPv4Address remote_addr,
uint16_t remote_port,
uint16_t local_port,
uint8_t ttl,
IPv4Address local_addr = 0):
remote_addr_(remote_addr),
remote_port_(remote_port),
local_port_(local_port),
ttl_(ttl),
local_addr_(local_addr) { };
IP& send();
};

#endif /* _UDPV4PROBE_H */

176 changes: 53 additions & 123 deletions src/dublin_traceroute.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern int errno;
#include <tins/utils.h>

#include "dublintraceroute/dublin_traceroute.h"
#include "dublintraceroute/udpv4probe.h"

/*
* Dublin Traceroute
Expand Down Expand Up @@ -93,109 +94,6 @@ const void DublinTraceroute::validate_arguments() {
}
}

/** \brief Method that generates and returns the packets to send
*
* This method generates a map containing the packets to send in order to run a
* multipath traceroute. The packets are grouped by flow, each identified by a
* destination port. Each flow specifies a vector of IP packets, and its
* position determines the TTL used for the packet. E.g. the packet at position
* 0 has TTL 1, and so on.
* The number of flows is determined by npaths(), which is passed to the
* constructor.
*
* \sa traceroute()
* \sa npaths()
*
* \return the packets to send
*/
std::shared_ptr<flow_map_t> DublinTraceroute::generate_per_flow_packets() {
NetworkInterface iface = NetworkInterface::default_interface();
NetworkInterface::Info info = iface.addresses();
std::shared_ptr<flow_map_t> flows(new flow_map_t);

/* The payload is used to manipulate the UDP checksum, that will be
* used as hop identifier.
* The last two bytes will be adjusted to influence the hop identifier,
* which for UDP traceroutes is the UDP checksum.
*/
unsigned char payload[] = {'N', 'S', 'M', 'N', 'C', 0x00, 0x00};

// Resolve the target host
// TODO support IPv6
try {
target(Utils::resolve_domain(dst()));
} catch (std::runtime_error) {
target(IPv4Address(dst()));
}

// check for valid min and max TTL
if (min_ttl_ > max_ttl_) {
throw std::invalid_argument("max_ttl must be greater or equal than min_ttl");
}

// forge the packets to send
for (uint16_t dport = dstport(); dport < dstport() + npaths(); dport++) {
hops_t hops(new std::vector<Hop>());
flows->insert(std::make_pair(dport, hops));
/* Forge the packets to send and append them to the packets
* vector.
* To force a packet through the same network flow, it has to
* maintain several constant fields that will be used for the
* ECMP hashing. These fields are, in the case of IP+UDP:
*
* IPv4.tos
* IPv4.proto
* IPv4.src
* IPv4.dst
* UDP.sport
* UDP.dport
*/
for (uint8_t ttl = min_ttl_; ttl <= max_ttl_; ttl++) {
/*
* Adjust the payload for each flow to obtain the same UDP
* checksum. The UDP checksum is used to identify the flow.
*/
uint16_t identifier = dport + ttl;
payload[5] = ((unsigned char *)&identifier)[0];
payload[6] = ((unsigned char *)&identifier)[1];

IP packet = IP(target(), info.ip_addr) /
UDP(dport, srcport()) /
RawPDU((char *)payload);
packet.ttl(ttl);
packet.flags(IP::DONT_FRAGMENT); // set DF bit

// serialize the packet so that we can extract src IP
// and checksum
packet.serialize();

// get our own IPv4 address - will be used by the sniffer thread to
// sniff only the relevant traffic
if (my_address == "0.0.0.0")
my_address = IPv4Address(packet.src_addr());
else {
if (packet.src_addr() != my_address) {
std::stringstream ss;
ss << "Packets flowing through more than one interface, " << my_address << " and " << packet.src_addr();
throw DublinTracerouteException(ss.str());
}
}
packet.id(packet.rfind_pdu<UDP>().checksum());

try {
Hop hop;
hop.sent(packet);
hops->push_back(hop);
} catch (std::runtime_error e) {
std::stringstream ss;
ss << "Cannot find flow: " << dport << ": " << e.what();
throw DublinTracerouteException(ss.str());
}
}
}

return flows;
}

/** \brief run the multipath traceroute
*
Expand All @@ -206,14 +104,19 @@ std::shared_ptr<flow_map_t> DublinTraceroute::generate_per_flow_packets() {
* \sa TracerouteResults
* \returns an instance of TracerouteResults
*/
TracerouteResults &DublinTraceroute::traceroute() {
TracerouteResults& DublinTraceroute::traceroute() {
// avoid running multiple traceroutes
if (mutex_tracerouting.try_lock() == false)
throw DublinTracerouteInProgressException("Traceroute already in progress");

auto flows = generate_per_flow_packets();
validate_arguments();

TracerouteResults *results = new TracerouteResults(flows, min_ttl_, broken_nat());
// Resolve the target host
try {
target(Utils::resolve_domain(dst()));
} catch (std::runtime_error) {
target(IPv4Address(dst()));
}

uint16_t num_packets = (max_ttl() - min_ttl() + 1) * npaths();
std::chrono::steady_clock::time_point deadline = \
Expand Down Expand Up @@ -282,11 +185,53 @@ TracerouteResults &DublinTraceroute::traceroute() {
close(sock);
}
);
// send everything out
send_all(flows);

std::shared_ptr<flow_map_t> flows(new flow_map_t);

// forge the packets to send
for (uint16_t dport = dstport(); dport < dstport() + npaths(); dport++) {
/* Forge the packets to send and append them to the packets
* vector.
* To force a packet through the same network flow, it has to
* maintain several constant fields that will be used for the
* ECMP hashing. These fields are, in the case of IP+UDP:
*
* IPv4.tos
* IPv4.proto
* IPv4.src
* IPv4.dst
* UDP.sport
* UDP.dport
*/
hops_t hops(new std::vector<Hop>());
flows->insert(std::make_pair(dport, hops));
for (uint8_t ttl = min_ttl_; ttl <= max_ttl_; ttl++) {
/*
* Adjust the payload for each flow to obtain the same UDP
* checksum. The UDP checksum is used to identify the flow.
*/
UDPv4Probe probe(target(), dport, srcport(), ttl);
auto packet = probe.send();
auto now = Tins::Timestamp::current_time();

try {
Hop hop;
hop.sent(packet);
hop.sent_timestamp(now);
hops->push_back(hop);
} catch (std::runtime_error e) {
std::stringstream ss;
ss << "Cannot find flow: " << dport << ": " << e.what();
throw DublinTracerouteException(ss.str());
}
std::this_thread::sleep_for(std::chrono::milliseconds(delay()));
}
}

listener_thread.join();

TracerouteResults *results = new TracerouteResults(flows, min_ttl_, broken_nat());

match_sniffed_packets(*results);
match_hostnames(*results, flows);

Expand All @@ -295,21 +240,6 @@ TracerouteResults &DublinTraceroute::traceroute() {
return *results;
}

void DublinTraceroute::send_all(std::shared_ptr<flow_map_t> flows) {
NetworkInterface iface = NetworkInterface::default_interface();
PacketSender sender;
for (auto &iter: *flows) {
auto packets = iter.second;
for (auto &hop: *packets) {
auto packet = hop.sent();

sender.send(*packet, iface.name());
hop.sent_timestamp(Tins::Timestamp::current_time());
std::this_thread::sleep_for(std::chrono::milliseconds(delay()));
}
}
}


bool DublinTraceroute::sniffer_callback(Packet &packet) {
std::lock_guard<std::mutex> lock(mutex_sniffed_packets);
Expand Down
60 changes: 60 additions & 0 deletions src/udpv4probe.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* \file udpv4probe.cc
* \Author Andrea Barberio <[email protected]>
* \date 2017
* \brief Definition of the UDPv4Probe class
*
* This file contains the definition of the UDPv4Probe class, which represents
* an UDP probe that will be sent over IPv4.
*
* \sa udpv4probe.h
*/

#include <memory>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <iostream>
#include <iomanip>

#include "dublintraceroute/udpv4probe.h"
#include "dublintraceroute/common.h"
#include "dublintraceroute/exceptions.h"
#include "dublintraceroute/icmp_messages.h"


/** \brief method that sends the probe to the specified destination
*/
IP& UDPv4Probe::send() {
/* The payload is used to manipulate the UDP checksum, that will be
* used as hop identifier.
* The last two bytes will be adjusted to influence the hop identifier,
* which for UDP traceroutes is the UDP checksum.
*/
unsigned char payload[] = {'N', 'S', 'M', 'N', 'C', 0x00, 0x00};

/* The identifier is used to identify and match a response packet to
* the corresponding sent packet
*/
uint16_t identifier = remote_port_ + ttl_;

payload[5] = ((unsigned char *)&identifier)[0];
payload[6] = ((unsigned char *)&identifier)[1];
IP *packet = new IP(remote_addr_, local_addr_) /
UDP(remote_port_, local_port_) /
RawPDU((char *)payload);
packet->ttl(ttl_);
packet->flags(IP::DONT_FRAGMENT);

// serialize the packet so we can extract source IP and checksum
packet->serialize();

packet->id(packet->rfind_pdu<UDP>().checksum());

NetworkInterface iface = NetworkInterface::default_interface();
PacketSender sender;
sender.send(*packet, iface.name());
return *packet;
}

0 comments on commit 942a421

Please sign in to comment.