$search
00001 #! /usr/bin/env python 00002 00003 import interface 00004 import event 00005 from twisted.internet import reactor 00006 from twisted.internet.defer import inlineCallbacks 00007 import ip_rule 00008 import config 00009 import time 00010 import sys 00011 import radio_manager 00012 import logging_config 00013 import socket 00014 import system 00015 import re 00016 from netlink_monitor import netlink_monitor, IFSTATE, RunCommand 00017 from async_helpers import mainThreadCallback 00018 00019 summary_logger = logging_config.get_logger_stream_for_file('console.summary') 00020 00021 class RULEID: 00022 LOCAL=100 00023 FIRST_IFACE=50 00024 TUNNEL=150 00025 BLOCK_TUNNEL=175 00026 DEFAULT=200 00027 BLOCK_NON_TUNNEL=250 00028 00029 class InterfaceSelector: 00030 def __init__(self): 00031 self.interfaces = {} 00032 self.update_event = event.Event() 00033 self.update_interval = 1 00034 self.radio_manager = radio_manager.RadioManager() 00035 self.inactive_penalty = config.get_parameter('inactive_penalty', 50) 00036 self.forced_interface = "" 00037 self.tunnel_interface = config.get_parameter('tunnel_interface', "") 00038 self.use_tunnel = True 00039 self.active_interfaces = [] 00040 00041 self.basestation = config.get_parameter('base_station') 00042 # print "Resolving basestation IP. (Blocking operation.)" 00043 # self.basestation_ip = socket.gethostbyname(config.get_parameter('base_station')) 00044 00045 # Add rules to guarantee that local routes go to the main table. 00046 # self.local_net_rule = ip_rule.IpRule(RULEID.LOCAL) 00047 # for subnet in config.get_parameter('local_networks'): 00048 # self.local_net_rule.add('to', subnet, 'lookup', 'main') 00049 00050 self.local_net_rules = [] 00051 for (i, subnet) in enumerate(config.get_parameter('local_networks')): 00052 self.local_net_rules.append(ip_rule.IpRule(RULEID.LOCAL+i)) 00053 self.local_net_rules[i].add('to', subnet, 'lookup', 'main') 00054 00055 # Add a rule to send through the vpn. 00056 if self.tunnel_interface: 00057 self.vpn_rule = ip_rule.IpRule(RULEID.DEFAULT) 00058 # Use LINK here because netlink_monitor's parsing rules don't 00059 # currently work on vpn interfaces. 00060 system.system('ip', 'route', 'flush', 'table', str(RULEID.DEFAULT)) 00061 netlink_monitor.get_state_publisher(self.tunnel_interface, 00062 IFSTATE.LINK).subscribe(self._refresh_default_route) 00063 00064 00065 # Create all the interfaces. 00066 interface_names = config.get_parameter('interfaces').keys() 00067 ifaceid = RULEID.FIRST_IFACE 00068 for iface in interface_names: 00069 try: 00070 new_iface = self.interfaces[iface] = interface.construct(iface, ifaceid) 00071 new_iface.score = InterfaceSelector.TERRIBLE_INTERFACE 00072 new_iface.prescore = InterfaceSelector.TERRIBLE_INTERFACE 00073 ifaceid += 1 00074 except interface.NoType: 00075 print >> sys.stderr, "Interface %s has no type."%iface 00076 sys.exit(1) 00077 except interface.UnknownType, e: 00078 print >> sys.stderr, "Interface %s has unknown type %s."%(iface, e) 00079 sys.exit(1) 00080 except: 00081 print >> sys.stderr, "Error creating interface %s."%iface 00082 raise 00083 00084 # Register the radios with the radio manager 00085 for i in self.interfaces.itervalues(): 00086 if isinstance(i, interface.WirelessInterface): 00087 self.radio_manager.add_iface(i) 00088 00089 # Prepare the rules that select a particular interface. 00090 self.tun_ip_rules = [ip_rule.IpRule(RULEID.TUNNEL+i) for i in range(len(self.interfaces) + 1)] 00091 00092 # Set up periodic updates, and run the first one. 00093 self.shutting_down = False 00094 self._periodic_update() 00095 reactor.addSystemEventTrigger('before', 'shutdown', self._shutdown) 00096 00097 def _shutdown(self): 00098 self.shutting_down = True 00099 # remove all of our routing rules 00100 for tir in self.tun_ip_rules: 00101 tir.set() 00102 for lnr in self.local_net_rules: 00103 lnr.set() 00104 00105 @mainThreadCallback 00106 def set_mode(self, ssid = "", bssid = "", sel_interface = "", use_tunnel = True, band = 3, scan_only = False): 00107 print >> sys.stderr, "Dynamic reconfiguration ssid: %s bssid: %s iface: %s tun: %s band: %s scan_only: %s"%(ssid, bssid, sel_interface, use_tunnel, band, scan_only) 00108 self.goodness_weight = config.get_parameter('ping_weighting', 0.5) 00109 self.radio_manager.set_mode(ssid, bssid, band, scan_only, sel_interface) 00110 self.forced_interface = sel_interface 00111 self.use_tunnel = use_tunnel 00112 if self.tunnel_interface: 00113 if use_tunnel: 00114 self.vpn_rule.set('lookup', str(RULEID.DEFAULT)) 00115 else: 00116 self.vpn_rule.set() 00117 00118 def _refresh_default_route(self, old_state, new_state): 00119 if new_state: 00120 system.system('ip', 'route', 'replace', 'table', str(RULEID.DEFAULT), 'default', "dev", self.tunnel_interface) 00121 00122 def _periodic_update(self): 00123 if self.shutting_down: 00124 return 00125 self.periodic_update_handle = reactor.callLater(self.update_interval, self._periodic_update) 00126 00127 # Update all the interfaces. 00128 for iface in self.interfaces.values(): 00129 iface.update(self.update_interval) 00130 00131 # Update the radio manager 00132 self.radio_manager.update() 00133 00134 # Rank the interfaces. 00135 self.rank_interfaces() 00136 00137 # Broadcast the fact that an update has just completed. 00138 self.update_event.trigger() 00139 00140 @inlineCallbacks 00141 def set_tun_rules(self, selected_interfaces): 00142 # Set the interfaces we are given in order. 00143 00144 use_tunnel = self.use_tunnel 00145 # If we can resolve the IP of the base station, use the tunnel 00146 # otherwise, things are probably bad. don't use the tunnel 00147 # - if the tunnel (or DNS over the tunnel) is broken, this will 00148 # diable the tunnel 00149 # - if we didn't have DNS and it starts working, this will enable 00150 # the tunnel 00151 # - the basestation is a raw IP, this should always succeed and enable 00152 # the tunnel 00153 if use_tunnel: 00154 try: 00155 self.basestation_ip = socket.gethostbyname(self.basestation) 00156 except socket.error: 00157 use_tunnel = False 00158 00159 for i, iface in enumerate(selected_interfaces): 00160 if use_tunnel: 00161 self.tun_ip_rules[i].set('to', self.basestation_ip, 'lookup', iface.tableid) 00162 else: 00163 self.tun_ip_rules[i].set('lookup', iface.tableid) 00164 00165 last_rule = len(selected_interfaces) 00166 # set up blackhole rule if we're using a tunnel 00167 if use_tunnel: 00168 self.tun_ip_rules[last_rule].set('to', self.basestation_ip, 'blackhole') 00169 last_rule += 1 00170 00171 # Clear the remaining rules. 00172 for tir in self.tun_ip_rules[last_rule:]: 00173 yield tir.set() 00174 00175 TERRIBLE_INTERFACE = -1e1000 00176 00177 def score_interface(self, iface): 00178 # score is used for selecting the interface. 00179 # prescore is used by radio manager to decide which interface to 00180 # activate. 00181 00182 if iface.goodness <= 0 and self.forced_interface != iface.iface: 00183 iface.prescore = iface.score = InterfaceSelector.TERRIBLE_INTERFACE 00184 return 00185 00186 # If an interface is being forced, other interfaces 00187 # should all have terrible scores. 00188 if self.forced_interface and self.forced_interface != iface.iface: 00189 iface.prescore = iface.score = InterfaceSelector.TERRIBLE_INTERFACE 00190 return 00191 00192 iface.prescore = iface.score = self.goodness_weight * iface.goodness + (1 - self.goodness_weight) * iface.reliability + iface.priority 00193 if not iface.active: 00194 iface.score -= self.inactive_penalty 00195 00196 def rank_interfaces(self): 00197 # Score interfaces 00198 interfaces = self.interfaces.values() 00199 for iface in interfaces: 00200 self.score_interface(iface) 00201 00202 # Sort, and forget about the scores 00203 interfaces.sort(key = lambda iface: iface.score, reverse = True) 00204 active_interfaces = [ iface for iface in interfaces if iface.score != self.TERRIBLE_INTERFACE ] 00205 00206 for iface in interfaces: 00207 if iface in active_interfaces: 00208 iface.rank = active_interfaces.index(iface) 00209 else: 00210 iface.rank = -1 00211 00212 # Set the interfaces 00213 self.set_tun_rules(active_interfaces) 00214 00215 # Print active_iface status 00216 now = time.time() 00217 print >> summary_logger 00218 print >> summary_logger, time.ctime(now), now 00219 #print >> summary_logger, netlink_monitor.get_status_publisher(self.tunnel_interface).get() 00220 for rank, iface in enumerate(interfaces): 00221 # FIXME 00222 iface.timeout_time = now 00223 active = "active" if iface.active else "" 00224 rule = "rule " if iface in active_interfaces else "norule" 00225 print >> summary_logger, "#% 2i %10.10s %7.1f %7.3f %7.3f %17.17s %7.3f %3.0f %s %s"% \ 00226 (rank, iface.prettyname, (iface.timeout_time - now), iface.score, iface.prescore, iface.bssid, iface.goodness, iface.reliability, rule, active) 00227 00228 self.active_interfaces = active_interfaces