| 1 |  |  | #!/usr/bin/env python | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | from __future__ import print_function | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | import sys | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | import json | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | import socket | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | import time | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | import tracerouteparser | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | import signal | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | from requests import post, get, ConnectionError | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | from subprocess import check_output, Popen, PIPE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | from multiprocessing import Pool | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | import logging | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | ON_POSIX = 'posix' in sys.builtin_module_names | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | def init_worker(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |     signal.signal(signal.SIGINT, signal.SIG_IGN) | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  | def own_ips(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |     return check_output(["ip", "-4", "addr", "list"]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  | global OWN_IPS | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  | OWN_IPS = own_ips() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | def ext_ip(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |     We make the assumtion that we ony have one gateway to any one destination, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |     could be better to use "ip route get $ip" but that won't cover NAT scenario | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |     try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |         resp = get('https://httpbin.org/ip') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |         return resp.json()["origin"] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |     except ConnectionError as e: | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |         logging.warning("Could not get external ip:\n" + str(e)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |         return None | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  | def submit_trace(ip_dict, result): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |     if result is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |         return | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  |     if ip_dict.get("simulate"): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |         print(json.dumps(result)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |         try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |             r = post(ip_dict["url"], data=json.dumps(result)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |             logging.debug(r) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |         except ConnectionError as e: | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |             print(e) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 56 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 57 |  |  | def run_trace(ip_dict): | 
            
                                                                        
                            
            
                                    
            
            
                | 58 |  |  |     ip = ip_dict["ip"] | 
            
                                                                        
                            
            
                                    
            
            
                | 59 |  |  |     if ip in OWN_IPS: | 
            
                                                                        
                            
            
                                    
            
            
                | 60 |  |  |         return None | 
            
                                                                        
                            
            
                                    
            
            
                | 61 |  |  |     try: | 
            
                                                                        
                            
            
                                    
            
            
                | 62 |  |  |         proc = Popen(["/usr/sbin/traceroute", ip, "30"], stdout=PIPE, stderr=PIPE) | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                        
                            
            
                                    
            
            
                | 63 |  |  |         out, err = proc.communicate() | 
            
                                                                        
                            
            
                                    
            
            
                | 64 |  |  |     except KeyboardInterrupt: | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                        
                            
            
                                    
            
            
                | 65 |  |  |         proc.terminate() | 
            
                                                                        
                            
            
                                    
            
            
                | 66 |  |  |         proc.kill() | 
            
                                                                        
                            
            
                                    
            
            
                | 67 |  |  |         return None | 
            
                                                                        
                            
            
                                    
            
            
                | 68 |  |  |     src_ip = check_output(["ip", "route", "get", ip]).splitlines()[0].split()[-1] | 
            
                                                                        
                            
            
                                    
            
            
                | 69 |  |  |     trp = tracerouteparser.TracerouteParser() | 
            
                                                                        
                            
            
                                    
            
            
                | 70 |  |  |     trp.parse_data(out) | 
            
                                                                        
                            
            
                                    
            
            
                | 71 |  |  |     data = {} | 
            
                                                                        
                            
            
                                    
            
            
                | 72 |  |  |     data["dst_ip"] = trp.dest_ip | 
            
                                                                        
                            
            
                                    
            
            
                | 73 |  |  |     data["src_ip"] = src_ip | 
            
                                                                        
                            
            
                                    
            
            
                | 74 |  |  |     data["dst_name"] = trp.dest_name | 
            
                                                                        
                            
            
                                    
            
            
                | 75 |  |  |     data["hops"] = {} | 
            
                                                                        
                            
            
                                    
            
            
                | 76 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 77 |  |  |     for hop in trp.hops: | 
            
                                                                        
                            
            
                                    
            
            
                | 78 |  |  |         data["hops"][int(hop.idx)] = [] | 
            
                                                                        
                            
            
                                    
            
            
                | 79 |  |  |         for probe in hop.probes: | 
            
                                                                        
                            
            
                                    
            
            
                | 80 |  |  |             data["hops"][int(hop.idx)].append({"name": probe.name, | 
            
                                                                        
                            
            
                                    
            
            
                | 81 |  |  |                                                "ip": probe.ipaddr, | 
            
                                                                        
                            
            
                                    
            
            
                | 82 |  |  |                                                "rtt": probe.rtt, | 
            
                                                                        
                            
            
                                    
            
            
                | 83 |  |  |                                                "anno": probe.anno}) | 
            
                                                                        
                            
            
                                    
            
            
                | 84 |  |  |     ret = {"reporter": socket.gethostname(), | 
            
                                                                        
                            
            
                                    
            
            
                | 85 |  |  |            "note": ip_dict["note"], | 
            
                                                                        
                            
            
                                    
            
            
                | 86 |  |  |            "ext_ip": ip_dict["ext_ip"], | 
            
                                                                        
                            
            
                                    
            
            
                | 87 |  |  |            "data": data} | 
            
                                                                        
                            
            
                                    
            
            
                | 88 |  |  |     submit_trace(ip_dict, ret) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  | def run_runner(config): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |     NUMPROCS = config.procs | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |     if config.debug: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |         logging.basicConfig(level=logging.DEBUG) | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |         logging.getLogger("requests").setLevel(logging.DEBUG) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |         logging.basicConfig(level=logging.WARNING) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |         logging.getLogger("requests").setLevel(logging.WARNING) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |     start_time = time.time() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |     logging.info("Traceroute runner starting") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |     if config.get("server_url", False): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |         URL = config.server_url + "/trace" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |         URL = "http://127.0.0.1:9001" + "/trace" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  |     if config.get("ips", False): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |         ips = config.ips | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |         if config.get("ips_file", False): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |             logging.warning("-i overrides ips from file with -f") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |     elif config.ips_file: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |         ips = [] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |         with open(config.ips_file) as f: | 
                            
                    |  |  |  | 
                                                                                        
                                                                                            
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |             for line in f: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |                 ips.append(line.strip()) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |     else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |         logging.error("No ips defined, exiting") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |         sys.exit(1) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  | # The only reason we need to pack all this info into a list is that | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  | # we only get to pass one iterable to function from map_async or | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  | # thats how I (poorly) understand it | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |     ips_to_iter = [] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |     detected_ext_ip = ext_ip() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |     for ip in ips: | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |         ip_dict = {} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |         ip = str(ip) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |         ip_dict["note"] = config.get("note", None) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |         ip_dict["ext_ip"] = detected_ext_ip | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |         ip_dict["url"] = URL | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |         ip_dict["ip"] = ip | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |         ip_dict["simulate"] = config.get("simulate", None) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |         ips_to_iter.append(ip_dict) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |     logging.debug('IP addresses: ' + str(ips)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |     logging.debug(str(ips_to_iter)) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |     try: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |         pool = Pool(NUMPROCS, init_worker) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |         pool.map_async(run_trace, ips_to_iter, 1).get(9999999) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |         pool.close() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |         pool.join() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |     except KeyboardInterrupt: | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |         print("Caught KeyboardInterrupt, terminating workers") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |         pool.terminate() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |         pool.join() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |         sys.exit(1) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |     end_time = time.time() | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 152 |  |  |     logging.info("Traceroute runner done, Took: " + str(int(end_time - start_time)) + " seconds") | 
            
                                                        
            
                                    
            
            
                | 153 |  |  |  | 
            
                        
It is generally discouraged to redefine built-ins as this makes code very hard to read.