$search
00001 #! /usr/bin/env python 00002 00003 import urllib, urllib2, base64, httplib 00004 import string, math, re, time 00005 00006 import roslib; roslib.load_manifest('ddwrt_access_point') 00007 import rospy 00008 00009 import dynamic_reconfigure.server 00010 from access_point_control.cfg import ApControlConfig 00011 from ieee80211_channels.channels import IEEE80211_Channels 00012 00013 MAX_HTTP_REQUEST_TRIES=10 00014 INTERVAL_BETWEEN_TRIES=1.0 # seconds 00015 00016 class IncompleteResponseBody(Exception): 00017 def __str__(self): 00018 return "Could not find </html> in response body" 00019 00020 class DdwrtApControl: 00021 00022 def __init__(self, hostname, username, password, interface = "wl0"): 00023 self.hostname = hostname 00024 self.username = username 00025 self.password = password 00026 self.interface = interface 00027 if interface == "wl0": 00028 self.other_interface = "wl1" 00029 else: 00030 self.other_interface = "wl0" 00031 00032 self.passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() 00033 self.passmgr.add_password(realm=None, 00034 uri="http://%s/"%(hostname), 00035 user=username, 00036 passwd=password) 00037 self.auth_handler = urllib2.HTTPBasicAuthHandler(self.passmgr) 00038 self.opener = urllib2.build_opener(self.auth_handler) 00039 urllib2.install_opener(self.opener) 00040 00041 # dd-wrt WRT610n specific config 00042 if self.interface == "wl0": 00043 self.band = IEEE80211_Channels.BAND_2400_MHz 00044 else: 00045 self.band = IEEE80211_Channels.BAND_5000_MHz 00046 self.min_txpower_mw = 1 00047 self.max_txpower_mw = 251 00048 00049 self.current_config = {} 00050 self.get_current_config() 00051 00052 node_name = rospy.get_name() 00053 for param_name in self.current_config: 00054 param_full_name = node_name + "/" + param_name 00055 if not rospy.has_param(param_full_name): 00056 rospy.set_param(param_full_name, self.current_config[param_name]) 00057 00058 def mw_to_dbm(self, mw): 00059 return int(10 * math.log10(float(mw)) + 0.5) 00060 00061 def dbm_to_mw(self, dbm): 00062 return int(math.pow(10, float(dbm) / 10.0) + 0.5) 00063 00064 def send_http_request(self, req): 00065 count = 0 00066 00067 while True: 00068 count += 1 00069 try: 00070 resp = urllib2.urlopen(req, timeout = 5.0) 00071 body = resp.read() 00072 if not re.findall("</html>", body): 00073 raise IncompleteResponseBody() 00074 break 00075 except (urllib2.URLError, httplib.BadStatusLine, IncompleteResponseBody), e: 00076 if count < MAX_HTTP_REQUEST_TRIES: 00077 rospy.logwarn("HTTP request failed attempting again: %s", str(e)) 00078 time.sleep(INTERVAL_BETWEEN_TRIES) 00079 continue 00080 else: 00081 raise e 00082 00083 return body 00084 00085 def apply_wrt_request(self, req_args, page): 00086 req = urllib2.Request("http://%s/apply.cgi"%(self.hostname)) 00087 00088 req.add_data(urllib.urlencode(req_args)) 00089 00090 auth = self.passmgr.find_user_password(None, "http://%s/apply.cgi"%(self.hostname)) 00091 base64string = base64.encodestring("%s:%s" % auth)[:-1] 00092 req.add_header("Authorization", "Basic %s" % base64string) 00093 req.add_header("Referer", "http://%s/%s.asp"%(self.hostname, page)) 00094 00095 try: 00096 self.send_http_request(req) 00097 except (urllib2.URLError, httplib.BadStatusLine, IncompleteResponseBody), e: 00098 self.current_config['status'] = "FAIL" 00099 self.current_config['errmsg'] += "HTTP request failed: " + str(e) 00100 00101 def get_page_info(self, page): 00102 req = urllib2.Request("http://%s/%s.asp"%(self.hostname, page)) 00103 auth = self.passmgr.find_user_password(None, "http://%s/%s.asp"%(self.hostname, page)) 00104 base64string = base64.encodestring("%s:%s" % auth)[:-1] 00105 req.add_header("Authorization", "Basic %s" % base64string) 00106 00107 return self.send_http_request(req) 00108 00109 def find_ssid(self, interface, html): 00110 ssid = re.findall(r"<input[^>]*name=\"%s_ssid\"[^>]*value=[^>]*\"([^>]+?)\"[^>]*>"%(interface), html) 00111 if not ssid: 00112 return None 00113 return ssid[0] 00114 00115 def find_mode(self, interface, html, mode): 00116 mode_block = re.findall(r"(?s)<select[^>]*name=\"%s_%s\"(.*?)</select>"%(interface, mode), html) 00117 if not mode_block: 00118 return None 00119 mode_block = mode_block[0] 00120 mode = re.findall(r"(?s)<option[^>]*value=[^\">]*?\"([^>]+?)[\\\"][^>]*?selected", mode_block) 00121 if not mode: 00122 return None 00123 return mode[0] 00124 00125 def find_channel(self, interface, html): 00126 channel = re.findall(r"var %s_channel[^;]*?'(\d+)'"%(interface), html) 00127 if not channel: 00128 return None 00129 return int(channel[0]) 00130 00131 def find_bitrate(self, interface, html): 00132 bitrate_block = re.findall(r"(?s)<select[^>]*name=\"%s_rate\"(.*?)</select>"%(interface), html) 00133 if not bitrate_block: 00134 return None 00135 bitrate_block = bitrate_block[0] 00136 bitrate = re.findall(r"(?s)<option[^>]*value=[^\">]*?\"([^>]+?)[\\\"][^>]*?selected", bitrate_block) 00137 if not bitrate: 00138 return 0 00139 return int(bitrate[0]) 00140 00141 def find_txpower(self, interface, html): 00142 txpower = re.findall(r"<input[^>]*name=\"%s_txpwr\"[^>]*value=[^>]*\"([^>]+?)\"[^>]*>"%(interface), html) 00143 if not txpower: 00144 return None 00145 return int(txpower[0]) 00146 00147 def find_wmm(self, interface, html): 00148 wmm = re.findall(r"name=\"%s_wme\"[^>*]value=\"(.*?)\"[^>*]checked=\"checked\""%(self.interface), html) 00149 if not wmm: 00150 return None 00151 if wmm[0] == "on": 00152 return True 00153 elif wmm[0] == "off": 00154 return False 00155 else: 00156 return None 00157 00158 def find_wep_key(self, interface, html): 00159 wep_key = re.findall(r"<input[^>]*name=[^>]*%s_key1[^>]*value=[^>]*\"([^>]*?)\"[^>]*>"%(interface), html) 00160 if not wep_key: 00161 return None 00162 return wep_key[0] 00163 00164 def find_wpa_psk(self, interface, html): 00165 wpa_psk = re.findall(r"<input[^>]*name=[^>]*%s_wpa_psk[^>]*value=[^>]*\"([^>]*?)\"[^>]*>"%(interface), html) 00166 if not wpa_psk: 00167 return None 00168 return wpa_psk[0] 00169 00170 def find_txpower(self, interface, html): 00171 txpower = re.findall(r"<input[^>]*name=\"%s_txpwr\"[^>]*value=[^>]*\"([^>]+?)\"[^>]*>"%(interface), html) 00172 if not txpower: 00173 return None 00174 return int(txpower[0]) 00175 00176 def get_other_interfaces_args(self): 00177 extra_args = {} 00178 html = self.get_page_info("Wireless_Basic") 00179 interface_list = re.findall(r"<select[^>]*name=[^>]*\"([a-zA-Z0-9]+)_mode\"[^>]*>", html) 00180 00181 for interface in interface_list: 00182 if interface != self.interface: 00183 ssid = self.find_ssid(interface, html) 00184 mode = self.find_mode(interface, html, "mode") 00185 if mode is None: 00186 mode = "ap" 00187 if ssid is not None: 00188 extra_args["%s_mode"%(interface)] = mode 00189 extra_args["%s_ssid"%(interface)] = ssid 00190 00191 return extra_args 00192 00193 def get_wireless_basic_params(self): 00194 # mode & ssid & channel 00195 html = self.get_page_info("Wireless_Basic") 00196 00197 ssid = self.find_ssid(self.interface, html) 00198 mode = self.find_mode(self.interface, html, "net_mode") 00199 if mode is None: 00200 mode = "disabled" 00201 channel = self.find_channel(self.interface, html) 00202 00203 return mode, ssid, channel 00204 00205 def get_wireless_advanced_params(self): 00206 # bitrate & txpower & wmm 00207 html = self.get_page_info("Wireless_Advanced-%s"%(self.interface)) 00208 00209 bitrate = self.find_bitrate(self.interface, html) 00210 txpower = self.find_txpower(self.interface, html) 00211 wmm = self.find_wmm(self.interface, html) 00212 00213 return bitrate, txpower, wmm 00214 00215 def get_wireless_security_params(self): 00216 # encryption_mode | encryption_pass 00217 html = self.get_page_info("WL_WPATable") 00218 00219 enc = self.find_mode(self.interface, html, "security_mode") 00220 if not enc: 00221 encryption_mode_out=['disabled'] 00222 else: 00223 encryption_mode_out=[enc] 00224 00225 if enc.find("psk") > -1: 00226 if enc == "psk": 00227 enc_mode = ApControlConfig.ApControl_wpa 00228 elif enc == "psk2": 00229 enc_mode = ApControlConfig.ApControl_wpa2 00230 elif enc == "psk psk2": 00231 enc_mode = ApControlConfig.ApControl_wpa_wpa2 00232 else: 00233 return None, "" 00234 enc_pass = self.find_wpa_psk(self.interface, html) 00235 elif enc == "wep": 00236 enc_mode = ApControlConfig.ApControl_wep 00237 enc_pass = self.find_wep_key(self.interface, html) 00238 elif enc == "disabled": 00239 enc_mode = ApControlConfig.ApControl_open 00240 enc_pass = "" 00241 else: 00242 return None, "" 00243 00244 return enc_mode, enc_pass 00245 00246 def get_current_config(self): 00247 mode, ssid, channel = self.get_wireless_basic_params() 00248 00249 if mode is None: 00250 raise Exception("Could not read interface %s mode"%(self.interface)) 00251 if mode != "disabled" and ssid is None: 00252 raise Exception("Could not read interface %s ssid"%(self.interface)) 00253 if mode != "disabled" and channel is None: 00254 raise Exception("Could not read interface %s channel"%(self.interface)) 00255 00256 # enabled 00257 if mode == "disabled": 00258 self.current_config['enabled'] = False 00259 else: 00260 self.current_config['enabled'] = True 00261 00262 # mode 00263 if mode in ["a-only", "b-only", "g-only"]: 00264 self.current_config['mode'] = mode[0] 00265 self.current_config['ieee80211n'] = False 00266 elif mode == "bg-mixed": 00267 self.current_config['mode'] = "g" 00268 self.current_config['ieee80211n'] = False 00269 elif mode == "mixed" and self.band == IEEE80211_Channels.BAND_2400_MHz: 00270 self.current_config['mode'] = "g" 00271 self.current_config['ieee80211n'] = True 00272 elif mode == "mixed" and self.band == IEEE80211_Channels.BAND_5000_MHz: 00273 self.current_config['mode'] = "a" 00274 self.current_config['ieee80211n'] = True 00275 elif mode == "n-only": 00276 self.current_config['ieee80211n'] = True 00277 else: 00278 self.current_config['mode'] = "unknown" 00279 self.current_config['ieee80211n'] = False 00280 00281 #ssid 00282 self.current_config['ssid'] = ssid 00283 00284 #freq 00285 self.current_config['freq'] = float(IEEE80211_Channels.get_freq(channel, self.band)) 00286 00287 bitrate, txpower_mw, wmm = self.get_wireless_advanced_params() 00288 if mode != "disabled" and (bitrate is None or txpower_mw is None or wmm is None): 00289 raise Exception("Could not read bitrate or txpower_mw or wmm: %s, %s, %s"% 00290 (str(bitrate), str(txpower_mw), str(wmm))) 00291 00292 #bitrate 00293 self.current_config['bitrate'] = bitrate 00294 #txpower 00295 self.current_config['txpower_auto'] = False 00296 self.current_config['txpower'] = self.mw_to_dbm(txpower_mw) 00297 #wmm 00298 self.current_config['wmm'] = wmm 00299 00300 enc_mode, enc_pass = self.get_wireless_security_params() 00301 if mode != "disabled" and (enc_mode is None or enc_pass is None): 00302 raise Exception("Could not read encryption mode") 00303 00304 self.current_config['encryption_mode'] = enc_mode 00305 self.current_config['encryption_pass'] = enc_pass 00306 00307 def set_wireless_basic(self, ssid, net_mode, channel): 00308 req_args = {} 00309 req_args["change_action"] = "gozila_cgi" 00310 req_args["submit_button"] = "Wireless_Basic" 00311 req_args["submit_type"] = "save" 00312 00313 req_args["%s_mode"%(self.interface)] = "ap" 00314 req_args["%s_net_mode"%(self.interface)] = net_mode 00315 if ssid is not None: 00316 req_args["%s_ssid"%(self.interface)] = ssid 00317 if channel is not None: 00318 req_args["%s_channel"%(self.interface)] = channel 00319 00320 req_args.update(self.get_other_interfaces_args()) 00321 00322 #self.apply_wrt_request(req_args, "Wireless_Basic") # "Save" 00323 req_args["action"] = "ApplyTake" 00324 self.apply_wrt_request(req_args, "Wireless_Basic") # "Apply Settings" 00325 00326 def set_wireless_advanced(self, bitrate, txpower, wmm): 00327 req_args = {} 00328 req_args["change_action"] = "" 00329 req_args["commit"] = "1" 00330 req_args["interface"] = self.interface 00331 req_args["submit_button"] = "Wireless_Advanced-%s"%(self.interface) 00332 req_args["submit_type"] = "save" 00333 00334 req_args["%s_rate"%self.interface] = bitrate 00335 req_args["%s_txpwr"%(self.interface)] = txpower 00336 if wmm == True: 00337 req_args["%s_wme"%(self.interface)] = "on" 00338 elif wmm == False: 00339 req_args["%s_wme"%(self.interface)] = "off" 00340 00341 self.apply_wrt_request(req_args, "Wireless_Advanced-%s") # "Save" 00342 req_args["action"] = "ApplyTake" 00343 self.apply_wrt_request(req_args, "Wireless_Advanced-%s"%(self.interface)) 00344 00345 def set_wireless_security(self, encryption_mode, encryption_passkey): 00346 req_args = {} 00347 req_args["action"] = "ApplyTake" 00348 req_args["change_action"] = "gozila_cgi" 00349 req_args["submit_button"] = "WL_WPATable" 00350 req_args["submit_type"] = "save" 00351 # req_args["wl%d_security_mode"%(not int(self.interface[-1]))] = "disabled" 00352 if encryption_mode == ApControlConfig.ApControl_open: 00353 req_args["%s_security_mode"%(self.interface)] = "disabled" 00354 elif encryption_mode == ApControlConfig.ApControl_wep: 00355 req_args["%s_security_mode"%(self.interface)] = "wep" 00356 req_args["%s_WEP_key"%(self.interface)] = "" 00357 req_args["%s_key"%(self.interface)]= "1" 00358 req_args["%s_key1"%(self.interface)] = encryption_passkey 00359 req_args["%s_wep"%(self.interface)] = "restricted" 00360 req_args["%s_wep_bit"%(self.interface)] = "64" # supported: 64 or 128 bits 00361 elif encryption_mode in [ApControlConfig.ApControl_wpa, 00362 ApControlConfig.ApControl_wpa2, 00363 ApControlConfig.ApControl_wpa_wpa2]: 00364 if encryption_mode == ApControlConfig.ApControl_wpa: 00365 req_args["%s_security_mode"%(self.interface)] = "psk" 00366 elif encryption_mode == ApControlConfig.ApControl_wpa2: 00367 req_args["%s_security_mode"%(self.interface)] = "psk2" 00368 elif encryption_mode == ApControlConfig.ApControl_wpa_wpa2: 00369 req_args["%s_security_mode"%(self.interface)] = "psk psk2" 00370 req_args["%s_crypto"%(self.interface)] = "tkip" 00371 req_args["%s_wpa_gtk_rekey"%(self.interface)] = "3600" 00372 req_args["%s_wpa_psk"%(self.interface)] = encryption_passkey 00373 else: 00374 self.current_config['status'] = "FAIL" 00375 self.current_config['errmsg'] += "encryption mode %s not supported"%(encryption_mode) 00376 return 00377 00378 #self.apply_wrt_request(req_args, "WL_WPATable") # "Save" 00379 #req_args["action"] = "ApplyTake" 00380 self.apply_wrt_request(req_args, "WL_WPATable") # "Apply Settings" 00381 00382 def compare_configs(self, requested_config, read_config): 00383 if requested_config['enabled'] != read_config['enabled']: 00384 self.current_config['status'] = "FAIL" 00385 self.current_config['errmsg'] += "Could not set enabled status, wrote %s, read %s"% \ 00386 (requested_config['enabled'], read_config['enabled']) 00387 return 00388 00389 if read_config['enabled']: 00390 for prop in ['mode', 'ssid', 'freq', 'ieee80211n', 'txpower', 'bitrate', 'wmm', 'encryption_mode']: 00391 if requested_config[prop] != read_config[prop]: 00392 self.current_config['status'] = "FAIL" 00393 self.current_config['errmsg'] += "Could not set %s, wrote %s, read %s"% \ 00394 (prop, str(requested_config[prop]), str(read_config[prop])) 00395 00396 if read_config['encryption_mode'] != "open": 00397 if requested_config['encryption_pass'] != read_config['encryption_pass']: 00398 self.current_config['status'] = "FAIL" 00399 self.current_config['errmsg'] += "Could not set encryption pass, wrote %s, read %s"% \ 00400 (requested_config['encryption_pass'], read_config['encryption_pass']) 00401 00402 def reconfigure(self, config, level): 00403 self.current_config['status'] = "OK" 00404 self.current_config['errmsg'] = "" 00405 00406 if config['enabled'] and \ 00407 ((config['mode'] == "a" and self.band != IEEE80211_Channels.BAND_5000_MHz) or \ 00408 ((config['mode'] == "b" or config['mode'] == "g") and self.band != IEEE80211_Channels.BAND_2400_MHz)): 00409 config['enabled'] = False 00410 self.current_config['status'] = "FAIL" 00411 self.current_config['errmsg'] = "Cannot set 802.11%s mode for interface in %s"%(config['mode'], self.band) 00412 00413 change = False 00414 # enabled 00415 if not config['enabled']: 00416 if self.current_config['enabled']: 00417 self.set_wireless_basic(None, "disabled", None) 00418 change = True 00419 else: 00420 # bitrate, txpower, wmm 00421 if config['txpower'] != self.current_config['txpower'] or \ 00422 config['bitrate'] != self.current_config['bitrate'] or \ 00423 config['wmm'] != self.current_config['wmm']: 00424 00425 new_txpower_mw = self.dbm_to_mw(config['txpower']) 00426 if new_txpower_mw > self.max_txpower_mw: 00427 config['txpower'] = self.mw_to_dbm(self.max_txpower_mw) 00428 if new_txpower_mw < self.min_txpower_mw: 00429 config['txpower'] = self.mw_to_dbm(self.min_txpower_mw) 00430 00431 self.set_wireless_advanced(config['bitrate'], self.dbm_to_mw(config['txpower']), config['wmm']) 00432 change = True 00433 00434 # security params: 00435 if config['encryption_mode'] != self.current_config['encryption_mode'] or \ 00436 (config['encryption_mode'] != "open" and \ 00437 config['encryption_pass'] != self.current_config['encryption_pass']): 00438 self.set_wireless_security(config['encryption_mode'], config['encryption_pass']) 00439 change = True 00440 00441 # ssid, freq, mode, ieee80211n 00442 if config['enabled'] != self.current_config['enabled'] or \ 00443 config['ssid'] != self.current_config['ssid'] or \ 00444 config['freq'] != self.current_config['freq'] or \ 00445 config['mode'] != self.current_config['mode'] or \ 00446 config['ieee80211n'] != self.current_config['ieee80211n']: 00447 new_channel = IEEE80211_Channels.get_channel(config['freq']) 00448 if not config['enabled']: 00449 new_mode = "disabled" 00450 elif config['ieee80211n']: 00451 new_mode = "mixed" 00452 else: 00453 new_mode = config['mode'] + "-only" 00454 00455 self.set_wireless_basic(config['ssid'], new_mode, new_channel) 00456 change = True 00457 00458 # verify config 00459 if change: 00460 self.get_current_config() 00461 self.compare_configs(config, self.current_config) 00462 00463 if self.current_config['enabled']: 00464 return self.current_config 00465 else: 00466 config['status'] = self.current_config['status'] 00467 config['errmsg'] = self.current_config['errmsg'] 00468 config['enabled'] = False 00469 return config 00470 00471 if __name__ == "__main__": 00472 rospy.init_node("ddwrt_apcontrol_node") 00473 00474 ip = rospy.get_param("~ip", "192.168.1.1") 00475 user = rospy.get_param("~user", "root") 00476 password = rospy.get_param("~password", "admin") 00477 interface = rospy.get_param("~interface", "wl0") 00478 00479 ap = DdwrtApControl(ip, user, password, interface) 00480 00481 dynamic_reconfigure.server.Server(ApControlConfig, ap.reconfigure) 00482 00483 rospy.spin()