1## This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <phil@secdev.org> 4## This program is published under a GPLv2 license 5 6""" 7Routing and handling of network interfaces. 8""" 9 10 11from __future__ import absolute_import 12 13 14import scapy.consts 15from scapy.config import conf 16from scapy.error import Scapy_Exception, warning 17from scapy.modules import six 18from scapy.utils import atol, ltoa, itom, pretty_list 19 20 21############################## 22## Routing/Interfaces stuff ## 23############################## 24 25class Route: 26 def __init__(self): 27 self.resync() 28 self.invalidate_cache() 29 30 def invalidate_cache(self): 31 self.cache = {} 32 33 def resync(self): 34 from scapy.arch import read_routes 35 self.invalidate_cache() 36 self.routes = read_routes() 37 38 def __repr__(self): 39 rtlst = [] 40 for net, msk, gw, iface, addr, metric in self.routes: 41 rtlst.append((ltoa(net), 42 ltoa(msk), 43 gw, 44 (iface.name if not isinstance(iface, six.string_types) else iface), 45 addr, 46 str(metric))) 47 48 return pretty_list(rtlst, 49 [("Network", "Netmask", "Gateway", "Iface", "Output IP", "Metric")]) 50 51 def make_route(self, host=None, net=None, gw=None, dev=None, metric=1): 52 from scapy.arch import get_if_addr 53 if host is not None: 54 thenet,msk = host,32 55 elif net is not None: 56 thenet,msk = net.split("/") 57 msk = int(msk) 58 else: 59 raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") 60 if gw is None: 61 gw="0.0.0.0" 62 if dev is None: 63 if gw: 64 nhop = gw 65 else: 66 nhop = thenet 67 dev, ifaddr, _ = self.route(nhop) 68 else: 69 ifaddr = get_if_addr(dev) 70 return (atol(thenet), itom(msk), gw, dev, ifaddr, metric) 71 72 def add(self, *args, **kargs): 73 """Ex: 74 add(net="192.168.1.0/24",gw="1.2.3.4") 75 """ 76 self.invalidate_cache() 77 self.routes.append(self.make_route(*args,**kargs)) 78 79 80 def delt(self, *args, **kargs): 81 """delt(host|net, gw|dev)""" 82 self.invalidate_cache() 83 route = self.make_route(*args,**kargs) 84 try: 85 i=self.routes.index(route) 86 del(self.routes[i]) 87 except ValueError: 88 warning("no matching route found") 89 90 def ifchange(self, iff, addr): 91 self.invalidate_cache() 92 the_addr,the_msk = (addr.split("/")+["32"])[:2] 93 the_msk = itom(int(the_msk)) 94 the_rawaddr = atol(the_addr) 95 the_net = the_rawaddr & the_msk 96 97 98 for i, route in enumerate(self.routes): 99 net, msk, gw, iface, addr, metric = route 100 if scapy.consts.WINDOWS: 101 if iff.guid != iface.guid: 102 continue 103 elif iff != iface: 104 continue 105 if gw == '0.0.0.0': 106 self.routes[i] = (the_net,the_msk,gw,iface,the_addr,metric) 107 else: 108 self.routes[i] = (net,msk,gw,iface,the_addr,metric) 109 conf.netcache.flush() 110 111 112 113 def ifdel(self, iff): 114 self.invalidate_cache() 115 new_routes=[] 116 for rt in self.routes: 117 if scapy.consts.WINDOWS: 118 if iff.guid == rt[3].guid: 119 continue 120 elif iff == rt[3]: 121 continue 122 new_routes.append(rt) 123 self.routes=new_routes 124 125 def ifadd(self, iff, addr): 126 self.invalidate_cache() 127 the_addr,the_msk = (addr.split("/")+["32"])[:2] 128 the_msk = itom(int(the_msk)) 129 the_rawaddr = atol(the_addr) 130 the_net = the_rawaddr & the_msk 131 self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr,1)) 132 133 134 def route(self,dest,verbose=None): 135 if isinstance(dest, list) and dest: 136 dest = dest[0] 137 if dest in self.cache: 138 return self.cache[dest] 139 if verbose is None: 140 verbose=conf.verb 141 # Transform "192.168.*.1-5" to one IP of the set 142 dst = dest.split("/")[0] 143 dst = dst.replace("*","0") 144 while True: 145 l = dst.find("-") 146 if l < 0: 147 break 148 m = (dst[l:]+".").find(".") 149 dst = dst[:l]+dst[l+m:] 150 151 152 dst = atol(dst) 153 pathes=[] 154 for d,m,gw,i,a,me in self.routes: 155 if not a: # some interfaces may not currently be connected 156 continue 157 aa = atol(a) 158 if aa == dst: 159 pathes.append( 160 (0xffffffff, 1, (scapy.consts.LOOPBACK_INTERFACE, a, "0.0.0.0")) 161 ) 162 if (dst & m) == (d & m): 163 pathes.append((m, me, (i,a,gw))) 164 if not pathes: 165 if verbose: 166 warning("No route found (no default route?)") 167 return scapy.consts.LOOPBACK_INTERFACE, "0.0.0.0", "0.0.0.0" 168 # Choose the more specific route 169 # Sort by greatest netmask 170 pathes.sort(key=lambda x: x[0], reverse=True) 171 # Get all pathes having the (same) greatest mask 172 pathes = [i for i in pathes if i[0] == pathes[0][0]] 173 # Tie-breaker: Metrics 174 pathes.sort(key=lambda x: x[1]) 175 # Return interface 176 ret = pathes[0][2] 177 self.cache[dest] = ret 178 return ret 179 180 def get_if_bcast(self, iff): 181 for net, msk, gw, iface, addr, metric in self.routes: 182 if net == 0: 183 continue 184 if scapy.consts.WINDOWS: 185 if iff.guid != iface.guid: 186 continue 187 elif iff != iface: 188 continue 189 bcast = atol(addr)|(~msk&0xffffffff); # FIXME: check error in atol() 190 return ltoa(bcast) 191 warning("No broadcast address found for iface %s\n", iff); 192 193conf.route=Route() 194 195iface = conf.route.route("0.0.0.0", verbose=0)[0] 196 197# Warning: scapy.consts.LOOPBACK_INTERFACE must always be used statically, because it 198# may be changed by scapy/arch/windows during execution 199 200if (iface.name if hasattr(iface, "name") else iface) == scapy.consts.LOOPBACK_INTERFACE: 201 from scapy.arch import get_working_if 202 conf.iface = get_working_if() 203else: 204 conf.iface = iface 205 206del iface 207