commit 2e5647cf4a5de7ba83c3c12596661f9007c216b5 Author: Arturo Filastò art@fuffa.org Date: Fri Mar 7 13:42:52 2014 +0100
Move the parasitic traceroute function to the experimental tests. --- ooni/nettests/experimental/parasitictraceroute.py | 157 +++++---------------- ooni/nettests/manipulation/parasitictraceroute.py | 48 ------- 2 files changed, 38 insertions(+), 167 deletions(-)
diff --git a/ooni/nettests/experimental/parasitictraceroute.py b/ooni/nettests/experimental/parasitictraceroute.py index 631c24b..c8aa3ed 100644 --- a/ooni/nettests/experimental/parasitictraceroute.py +++ b/ooni/nettests/experimental/parasitictraceroute.py @@ -1,129 +1,48 @@ -# -*- encoding: utf-8 -*- -# -# :authors: Arturo Filastò -# :licence: see LICENSE - from twisted.python import usage -from twisted.internet import defer - +from twisted.internet import defer, reactor from ooni.templates import scapyt - -from scapy.all import * - from ooni.utils import log +from ooni.utils.txscapy import ParasiticTraceroute +from ooni.settings import config
-class UsageOptions(usage.Options): - optParameters = [['backend', 'b', 'google.com', 'Test backend to use'], - ['timeout', 't', 5, 'The timeout for the traceroute test'], - ['maxttl', 'm', 64, 'The maximum value of ttl to set on packets'], - ['dstport', 'd', 80, 'Set the destination port of the traceroute test'], - ['srcport', 'p', None, 'Set the source port to a specific value']] +from scapy.all import TCPerror, IPerror
-class ParasiticalTracerouteTest(scapyt.BaseScapyTest): - name = "Parasitic TCP Traceroute Test" - author = "Arturo Filastò" - version = "0.1" +class ParasiticTracerouteTest(scapyt.BaseScapyTest): + name = "Parasitic Traceroute Test" + description = "Injects duplicate TCP packets with varying TTL values by sniffing traffic" + version = '0.1'
- usageOptions = UsageOptions + samplePeriod = 40
def setUp(self): - def get_sport(): - if self.localOptions['srcport']: - return int(self.localOptions['srcport']) + self.report['parasitic_traceroute'] = {} + + def test_parasitic_traceroute(self): + self.pt = ParasiticTraceroute() + config.scapyFactory.registerProtocol(self.pt) + d = defer.Deferred() + reactor.callLater(self.samplePeriod, d.callback, self.pt) + return d + + def postProcessor(self, *args, **kwargs): + self.pt.stopListening() + self.report['received_packets'] = self.pt.received_packets + + for packet in self.pt.received_packets: + k = (packet[IPerror].id, packet[TCPerror].sport, packet[TCPerror].dport, packet[TCPerror].seq) + if k in self.pt.matched_packets: + ttl = self.pt.matched_packets[k]['ttl'] else: - return random.randint(1024, 65535) - self.get_sport = get_sport - - self.dst_ip = socket.gethostbyaddr(self.localOptions['backend'])[2][0] - - self.dport = int(self.localOptions['dstport']) - self.max_ttl = int(self.localOptions['maxttl']) - - @defer.inlineCallbacks - def test_parasitic_tcp_traceroute(self): - """ - Establishes a TCP stream, then sequentially sends TCP packets with - increasing TTL until we reach the ttl of the destination. - - Requires the backend to respond with an ACK to our SYN packet (i.e. - the port must be open) - - XXX this currently does not work properly. The problem lies in the fact - that we are currently using the scapy layer 3 socket. This socket makes - packets received be trapped by the kernel TCP stack, therefore when we - send out a SYN and get back a SYN-ACK the kernel stack will reply with - a RST because it did not send a SYN. - - The quick fix to this would be to establish a TCP stream using socket - calls and then "cannibalizing" the TCP session with scapy. - - The real fix is to make scapy use libpcap instead of raw sockets - obviously as we previously did... arg. - """ - sport = self.get_sport() - dport = self.dport - ipid = int(RandShort()) - - ip_layer = IP(dst=self.dst_ip, - id=ipid, ttl=self.max_ttl) - - syn = ip_layer/TCP(sport=sport, dport=dport, flags="S", seq=0) - - log.msg("Sending...") - syn.show2() - - synack = yield self.sr1(syn) - - log.msg("Got response...") - synack.show2() - - if not synack: - log.err("Got no response. Try increasing max_ttl") - return - - if synack[TCP].flags == 11: - log.msg("Got back a FIN ACK. The destination port is closed") - return - - elif synack[TCP].flags == 18: - log.msg("Got a SYN ACK. All is well.") - else: - log.err("Got an unexpected result") - return - - ack = ip_layer/TCP(sport=synack.dport, - dport=dport, flags="A", - seq=synack.ack, ack=synack.seq + 1) - - yield self.send(ack) - - self.report['hops'] = [] - # For the time being we make the assumption that we are NATted and - # that the NAT will forward the packet to the destination even if the TTL has - for ttl in range(1, self.max_ttl): - log.msg("Sending packet with ttl of %s" % ttl) - ip_layer.ttl = ttl - empty_tcp_packet = ip_layer/TCP(sport=synack.dport, - dport=dport, flags="A", - seq=synack.ack, ack=synack.seq + 1) - - answer = yield self.sr1(empty_tcp_packet) - if not answer: - log.err("Got no response for ttl %s" % ttl) - continue - - try: - icmp = answer[ICMP] - report = {'ttl': empty_tcp_packet.ttl, - 'address': answer.src, - 'rtt': answer.time - empty_tcp_packet.time - } - log.msg("%s: %s" % (dport, report)) - self.report['hops'].append(report) - - except IndexError: - if answer.src == self.dst_ip: - answer.show() - log.msg("Reached the destination. We have finished the traceroute") - return + ttl = 'unknown' + hop = (ttl, packet.src) + path = 'hops_%s' % packet[IPerror].dst + if path in self.report['parasitic_traceroute']: + self.report['parasitic_traceroute'][path].append(hop) + else: + self.report['parasitic_traceroute'][path] = [hop] + for p in self.report['parasitic_traceroute'].keys(): + self.report['parasitic_traceroute'][p].sort(key=lambda x: x[0]) + + self.report['sent_packets'] = self.pt.sent_packets + return self.report
diff --git a/ooni/nettests/manipulation/parasitictraceroute.py b/ooni/nettests/manipulation/parasitictraceroute.py deleted file mode 100644 index c8aa3ed..0000000 --- a/ooni/nettests/manipulation/parasitictraceroute.py +++ /dev/null @@ -1,48 +0,0 @@ -from twisted.python import usage -from twisted.internet import defer, reactor -from ooni.templates import scapyt -from ooni.utils import log -from ooni.utils.txscapy import ParasiticTraceroute -from ooni.settings import config - -from scapy.all import TCPerror, IPerror - -class ParasiticTracerouteTest(scapyt.BaseScapyTest): - name = "Parasitic Traceroute Test" - description = "Injects duplicate TCP packets with varying TTL values by sniffing traffic" - version = '0.1' - - samplePeriod = 40 - - def setUp(self): - self.report['parasitic_traceroute'] = {} - - def test_parasitic_traceroute(self): - self.pt = ParasiticTraceroute() - config.scapyFactory.registerProtocol(self.pt) - d = defer.Deferred() - reactor.callLater(self.samplePeriod, d.callback, self.pt) - return d - - def postProcessor(self, *args, **kwargs): - self.pt.stopListening() - self.report['received_packets'] = self.pt.received_packets - - for packet in self.pt.received_packets: - k = (packet[IPerror].id, packet[TCPerror].sport, packet[TCPerror].dport, packet[TCPerror].seq) - if k in self.pt.matched_packets: - ttl = self.pt.matched_packets[k]['ttl'] - else: - ttl = 'unknown' - hop = (ttl, packet.src) - path = 'hops_%s' % packet[IPerror].dst - if path in self.report['parasitic_traceroute']: - self.report['parasitic_traceroute'][path].append(hop) - else: - self.report['parasitic_traceroute'][path] = [hop] - for p in self.report['parasitic_traceroute'].keys(): - self.report['parasitic_traceroute'][p].sort(key=lambda x: x[0]) - - self.report['sent_packets'] = self.pt.sent_packets - return self.report -