Package: twisted-mtr
twisted-mtrA Python Twisted library that performs asynchronous high performance
traceroutes using mtr-packet.twisted-mtris designed to enable Twisted (as in thePython Twisted networking frameworkto perform
fully asynchronous IPv4 and IPv6 traceroutes.Installationtwisted-mtrrequires the Twisted library as a dependancy as well as the themtr-packetbinary to be available in your systems PATH. You can installtwisted-mtrvia pip:$pipinstalltwisted-mtrAny modern version of Python3 will be compatible.Formtr-packetthis is typically available from your systems package manager.
For example on Debian / Ubuntu based systems install themtr-tinypackage:# Will need to be run as root$aptinstallmtr-tinyFor Fedora / Redhat based systems this package is calledmtr:# Will need to be run as root$yuminstallmtrFor Alpine based systems this package is calledmtr:# Will need to be run as root$apkaddmtrConsult whatever package manager your system uses if it's not one of the above
examples. There may not be a suitable option for Windows systems and Windows
support has not been tested.Once you have Python, Twisted, thetwisted-mtrlibrary and themtr-packetbinary installed you are good to go.Usagetwisted-mtrrequires a source IP, either IPv4 or IPv6, as the source of your
traceroutes. This is not detected for you and needs to be manually set. It's
outside the scope of this library to detect your local IP. Specifying the IP
address also selects which physical or virtual network interface you want to
send the traceroutes from.A helper utility exists to help find the path to yourmtr-packetbinary.A basic example would be:importipaddressfromtwisted.internetimportreactorfromtwisted_mtrimportutils,mtr# Find mtr-packetmtr_binary_name='mtr-packet'mtr_binary_path=utils.find_binary(mtr_binary_name)# Replace with a local IPv4 addresslocal_ipv4=ipaddress.IPv4Address('10.11.22.33')# Address we're tracing totarget_ipv4=ipaddress.IPv4Address('1.1.1.1')# Create the Twisted Protocol instanceapp_mtr=mtr.TraceRoute(mtr_binary_path=mtr_binary_path,local_ipv4=local_ipv4)# Spawn the mtr-packet process attached to the protocolreactor.spawnProcess(app_mtr,mtr_binary_name,[mtr_binary_path],{})# Callback fired when the traceroute is completedeftraceroute_complete(target_ip,hops):print(f'Traceroute complete to{target_ip}in{len(hops)}hops')for(hop_num,hop_ip,microseconds)inhops:print(f' -{hop_num}{hop_ip}{microseconds}')# Trace complete, stop the reactorreactor.stop()# Callback fired if there's an errordeftrace_error(counter,joined_request,error,extra):print(f'Error running traceroute:{error}')# Error during traceroute, stop the reactorreactor.stop()# Start our trace with our callbacks setapp_mtr.trace(traceroute_complete,trace_error,target_ip)# Start the Twisted reactor to begin the traceroutereactor.run()Seeexample-trace.pyfor an example implementation with
multiple IPv4 and IPv6 traceroutes running concurrently.API synopsistwisted-mtrhas really only one class you would interact with attwisted_mtr.mtr.TraceRoutethat takes the following parameters:my_traceroute_object=TraceRoute(# Full path to your mtr-packet binarymtr_binary_path='/usr/bin/mtr-packet',# An IPv4Address object for your local (source) IPv4 addresslocal_ipv4=ipaddress.IPv4Adddress('127.2.3.4'),# An IPv6Address object for your local (source) IPv6 addresslocal_ipv6=ipaddress.IPv6Adddress('::1'))You may leavelocal_ipv4orlocal_ipv6out if your system only has IPv4
or IPv6 available, however at least one of them must be set or an exception
will be raised.You can, for obvious reasons, only send IPv4 traceroutes iflocal_ipv4is
set and you can only send IPv6 traceroutes iflocal_ipv6is set.If you set yourlocal_ipv*address incorrectly your traceroutes may trigger
the error callback with a network error or simply time out.Once yourTraceRouteobject has been created you start a traceroute with
the following method:my_traceroute_object.trace(# Must be a function that exists or a lambdasuccess_callback_function,# Must be a function that exists or a lambdafailure_callback_function,# An IPv4Address or IPv6Address object of the address to traceroute toipaddress.IPv4Address('1.1.1.1'),# The protocol to use, defaults to 'icmp', can be 'icmp' or 'tcp'protocol='tcp',# The port number to use if the protocol is 'tcp', otherwise ignoredport=443,# TTL to start the trace at, defaults to 1, set to 2 or more to skip hopsttl=1)When the traceroute completes or errors the callbacks will be called with the
following parameters:defsuccess_callback_function(timestamp,target_ip,protocol,port,hops):# target_ip is an IPvNAddress object of the address the traceroute was to# protocol is either 'icmp' or 'tcp'# port is the TCP port if the trace type is 'tcp', otherwise -1print(f'Completed trace started at{timestamp}to:{target_ip}'f'({protocol}:{port})')# hops is a list of the traceroute hops, each hop has 5 parameters, e.g.#hops = [#    (1, '10.0.0.1', 20),#    (2, '22.22.22.22', 111),#    (3, '33.33.33.33', 222),#    (4,  None, None),#    (5, '55.55.55.55', 444),#]# The IP and milliseconds of the hop may be None if the hop did not# respond to the traceroute request or it timed out. Parameter 4 is the# protocol of the trace. Prameter 5 is the port number if the protocol is# 'tcp'. Example TCP trace hop:#  (1, '10.0.0.1', 20, 'tcp', 443)forhopinhops:hop_number,hop_ip,latency_in_milliseconds=hopprint(f' -{hop}:{hop_ip}{latency_in_milliseconds}ms')deffailure_callback_function(hop_number,request,error,extra):# Errors are things like mtr-packet return a serious error for your# traceroute request or network errors, not Python errors.## hop_number is the traceroute hop where the error occured# request the mtr-packet request that generated the error# error is the error message as a string# extra is any addtional data that was bundled with the requestprint(f'An error occured at hop{hop_number}sending MTR request 'f'"{request}" with error:{error}')TestsThere is a test suite that you can run by cloning this repository, installing
the required dependancies and execuiting:$maketestDebuggingtwisted-mtrwill emit debug logs if you use Python's logging module. Enable
them withlevel=logging.DEBUGin your application when you initialise your
logger.ContributingAll properly formatted and sensible pull requests, issues and comments are
welcome.