$search
00001 #! /usr/bin/env python 00002 00003 import time 00004 import subprocess 00005 from array import array 00006 import os, sys, signal 00007 import socket, fcntl 00008 import struct 00009 import math 00010 00011 import roslib; roslib.load_manifest('hostapd_access_point') 00012 import rospy 00013 import dynamic_reconfigure.server 00014 from access_point_control.cfg import ApControlConfig 00015 from ieee80211_channels.channels import IEEE80211_Channels 00016 00017 # range for valid txpower search 00018 MIN_TXPOWER = 0 # dBm 00019 MAX_TXPOWER = 30 # dBm 00020 00021 IFNAMSIZ = 16 # interface name size 00022 MAX_SSID_LENGTH = 32 00023 00024 # ioctl calls from wireless.h 00025 SIOCGIWESSID = 0x8B1B # get ESSID 00026 SIOCSIWRATE = 0x8B20 # set default bit rate (bps) 00027 SIOCGIWFREQ = 0x8B05 # get channel/frequency (Hz) 00028 SIOCSIWTXPOW = 0x8B26 # set transmit power (dBm) 00029 SIOCGIWTXPOW = 0x8B27 # get transmit power (dBm) 00030 00031 class ApHostapd: 00032 def __init__(self, interface, ip = None, netmask = None, hostapd = "hostapd"): 00033 self.hostapd = hostapd 00034 self.conffile = "/var/run/hostapd_" + interface + ".conf" 00035 self.pidfile = "/var/run/hostapd_" + interface + ".pid" 00036 self.dumpfile = "/var/run/hostapd_" + interface + ".dump" 00037 self.pid = None 00038 00039 self.interface = interface 00040 self.interface_data = array('c', interface + '\0' * (IFNAMSIZ-len(interface))); 00041 self.ioctl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 00042 00043 subprocess.call(["ifconfig", self.interface, "up"]) 00044 if ip is not None: 00045 if netmask is not None: 00046 subprocess.call(["ifconfig", self.interface, ip, "netmask", netmask]) 00047 else: 00048 subprocess.call(["ifconfig", self.interface, ip]) 00049 00050 self.config = dict() 00051 self.paused = True 00052 00053 def isrunning(self): 00054 if self.pid is None: 00055 try: 00056 self.pid = self.get_hostapd_pid() 00057 except IOError as err: 00058 return False 00059 isrunning = os.path.exists("/proc/%d"%(self.pid)) 00060 if not isrunning: 00061 self.pid = None 00062 return isrunning 00063 00064 def get_wpa_flag(self): 00065 if self.config['encryption_mode'] == ApControlConfig.ApControl_wpa: 00066 return 1 00067 if self.config['encryption_mode'] == ApControlConfig.ApControl_wpa2: 00068 return 2 00069 return 3 00070 00071 def generate_conf(self): 00072 # create hostapd.conf 00073 f = open(self.conffile, 'w') 00074 00075 channel = IEEE80211_Channels.get_channel(self.config['freq']) 00076 00077 if channel < 0: 00078 raise ValueError(-1, "Frequency not a valid IEEE 802.11 channel") 00079 00080 if (self.config['mode'] == 'a' and \ 00081 IEEE80211_Channels.get_band_from_freq(self.config['freq']) != IEEE80211_Channels.BAND_5000_MHz) or \ 00082 ((self.config['mode'] == 'b' or self.config['mode'] =='g') and \ 00083 IEEE80211_Channels.get_band_from_freq(self.config['freq']) != IEEE80211_Channels.BAND_2400_MHz): 00084 raise ValueError("Requested frequency is not valid for 802.11" + self.config['mode'] + " mode") 00085 00086 f.write(""" 00087 driver=nl80211 00088 00089 logger_syslog=-1 00090 logger_syslog_level=2 00091 logger_stdout=-1 00092 logger_stdout_level=0 00093 00094 ctrl_interface=/var/run/hostapd 00095 00096 ignore_broadcast_ssid=0 00097 00098 own_ip_addr=127.0.0.1 00099 country_code=""" + self.config['country_code'] + """ 00100 00101 interface=""" + self.interface + """ 00102 ssid=""" + self.config['ssid'] + """ 00103 hw_mode=""" + self.config['mode'] + """ 00104 channel=""" + str(channel) + """ 00105 ieee80211n=""" + str(int(self.config['ieee80211n'])) + """ 00106 00107 dump_file=""" + self.dumpfile) 00108 00109 if self.config['encryption_mode'] == ApControlConfig.ApControl_open: 00110 f.write(""" 00111 auth_algs=1""") 00112 elif self.config['encryption_mode'] == ApControlConfig.ApControl_wep: 00113 f.write(""" 00114 auth_algs=1 00115 wep_default_key=0 00116 wep_key0=""" + self.config['encryption_pass']) 00117 elif self.config['encryption_mode'] in \ 00118 [ ApControlConfig.ApControl_wpa, 00119 ApControlConfig.ApControl_wpa2, 00120 ApControlConfig.ApControl_wpa_wpa2]: 00121 wpa_flag = self.get_wpa_flag() 00122 f.write(""" 00123 auth_algs=3 00124 wpa=""" + str(wpa_flag) + """ 00125 wpa_passphrase=""" + self.config['encryption_pass'] + """ 00126 wpa_pairwise=CCMP TKIP""") 00127 00128 if self.config['wmm']: 00129 f.write(""" 00130 00131 wmm_enabled=1 00132 00133 wmm_ac_bk_cwmax=10 00134 wmm_ac_bk_aifs=7 00135 wmm_ac_bk_txop_limit=0 00136 wmm_ac_bk_acm=0 00137 wmm_ac_be_aifs=3 00138 wmm_ac_be_txop_limit=0 00139 wmm_ac_be_acm=0 00140 wmm_ac_vi_aifs=2 00141 wmm_ac_vi_acm=0 00142 wmm_ac_vo_aifs=2 00143 wmm_ac_vo_acm=0 00144 """) 00145 if self.config['mode'] != "b": 00146 f.write(""" 00147 wmm_ac_bk_cwmin=4 00148 wmm_ac_be_cwmin=4 00149 wmm_ac_be_cwmax=10 00150 wmm_ac_vi_cwmin=3 00151 wmm_ac_vi_cwmax=4 00152 wmm_ac_vi_txop_limit=94 00153 wmm_ac_vo_cwmin=2 00154 wmm_ac_vo_cwmax=3 00155 wmm_ac_vo_txop_limit=47 00156 """) 00157 else: 00158 f.write(""" 00159 wmm_ac_bk_cwmin=5 00160 wmm_ac_be_cwmin=5 00161 wmm_ac_be_cwmax=7 00162 wmm_ac_vi_cwmin=4 00163 wmm_ac_vi_cwmax=5 00164 wmm_ac_vi_txop_limit=188 00165 wmm_ac_vo_cwmin=3 00166 wmm_ac_vo_cwmax=4 00167 wmm_ac_vo_txop_limit=102 00168 """) 00169 f.write("\n") 00170 f.close() 00171 00172 def restart_hostapd(self): 00173 self.stop_ap() 00174 self.start_ap() 00175 00176 def get_ioctl_param(self, ioctl_request): 00177 iwreq_data = array('c') 00178 iwreq_data.extend(self.interface_data) 00179 iwreq_data.extend(struct.pack("iBBH", 0, 0, 0, 0)) 00180 iwreq_data.extend('\0' * (32 - len(iwreq_data))) 00181 00182 fcntl.ioctl(self.ioctl_sock.fileno(), ioctl_request, iwreq_data) 00183 return struct.unpack("iBBH", iwreq_data[16:24]) 00184 00185 def start_ap(self): 00186 self.generate_conf() 00187 if self.isrunning(): 00188 raise Exception("hostapd already running") 00189 00190 p = subprocess.Popen([self.hostapd, self.conffile, "-B", "-P", self.pidfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 00191 out, err = p.communicate() 00192 ret = p.wait() 00193 if ret != 0: 00194 print err, out 00195 raise Exception(ret, out) 00196 00197 self.paused = False 00198 00199 def get_hostapd_pid(self): 00200 f = open(self.pidfile, 'r') 00201 pid = int(f.read()) 00202 f.close() 00203 return pid 00204 00205 def stop_ap(self): 00206 if self.isrunning(): 00207 os.kill(self.pid, signal.SIGTERM) 00208 else: 00209 raise Exception("hostapd is dead") 00210 00211 signal_time = time.time() 00212 while self.isrunning() and time.time() - signal_time < 3.0: 00213 time.sleep(0.1) 00214 00215 if self.isrunning(): 00216 rospy.logwarn("hostapd pid %d was not terminated by SIGTERM signal, wating some more...", 00217 self.pid) 00218 signal_time = time.time() 00219 while self.isrunning() and time.time() - signal_time < 7.0: 00220 time.sleep(0.1) 00221 if self.isrunning(): 00222 os.kill(self.pid, signal.SIGKILL) 00223 rospy.logerr("hostapd pid %d terminated by SIGKILL signal", self.pid) 00224 raise Exception("could not kill hostapd") 00225 00226 self.paused = True 00227 00228 def get_ssid(self): 00229 ssid_buf = array('c', '\0' * MAX_SSID_LENGTH) 00230 ssid_buf_ptr, ssid_buf_len = ssid_buf.buffer_info() 00231 buf_ref = struct.pack('Pi', ssid_buf_ptr, ssid_buf_len) 00232 00233 iwreq_data = array('c') 00234 iwreq_data.extend(self.interface_data) 00235 iwreq_data.extend(buf_ref) 00236 iwreq_data.extend('\0' * (32 - len(iwreq_data))) 00237 00238 fcntl.ioctl(self.ioctl_sock.fileno(), SIOCGIWESSID, iwreq_data) 00239 00240 return ssid_buf.tostring() 00241 00242 def set_txpower(self, txpower, fixed = 1): 00243 iwreq_data = array('c') 00244 iwreq_data.extend(self.interface_data) 00245 iwreq_data.extend(struct.pack("iBBH", txpower, fixed, 0, 0)) 00246 iwreq_data.extend('\0' * (32 - len(iwreq_data))) 00247 00248 fcntl.ioctl(self.ioctl_sock.fileno(), SIOCSIWTXPOW, iwreq_data) 00249 00250 def get_txpower(self): 00251 txpower, fixed, disable, flags = self.get_ioctl_param(SIOCGIWTXPOW) 00252 return txpower 00253 00254 def get_freq(self): 00255 iwreq_data = array('c') 00256 iwreq_data.extend(self.interface_data) 00257 iwreq_data.extend('\0' * (32 - len(iwreq_data))) 00258 00259 fcntl.ioctl(self.ioctl_sock.fileno(), SIOCGIWFREQ, iwreq_data) 00260 m, e = struct.unpack("ih", iwreq_data[16:22]) 00261 return float(m) * math.pow(10, e) 00262 00263 def set_bitrate(self, bitrate, fixed = 1): 00264 iwreq_data = array('c') 00265 iwreq_data.extend(self.interface_data) 00266 iwreq_data.extend(struct.pack("iBBH", bitrate, fixed, 0, 0)) 00267 iwreq_data.extend('\0' * (32 - len(iwreq_data))) 00268 status = fcntl.ioctl(self.ioctl_sock.fileno(), SIOCSIWRATE, iwreq_data) 00269 00270 def set_encryption(self, encryption_mode, encryption_pass): 00271 self.config['encryption_mode'] = encryption_mode 00272 self.config['encryption_pass'] = encryption_pass 00273 self.restart_hostapd() 00274 00275 def reconfigure(self, config, level): 00276 # save bitrate setting 00277 if 'bitrate' in self.config: 00278 old_bitrate = self.config['bitrate'] 00279 00280 # update with new parameters 00281 self.config.update(config) 00282 00283 # if any hostapd parameter changed, stop hostapd 00284 if 2 & level and not self.paused: 00285 self.stop_ap() 00286 00287 self.config['status'] = 'OK' 00288 self.config['errmsg'] = '' 00289 00290 if config['enabled'] and self.paused: 00291 try: 00292 self.start_ap() 00293 except Exception as e: 00294 self.config['enabled'] = False 00295 self.config['status'] = 'FAIL' 00296 self.config['errmsg'] = str(e) 00297 00298 if not config['enabled'] and not self.paused: 00299 self.stop_ap() 00300 00301 # tx-power 00302 if 4 & level: 00303 if config['txpower_auto']: 00304 self.set_txpower(-1, 0) 00305 self.config['txpower'] = self.get_txpower() 00306 else: 00307 txpower = config['txpower'] 00308 txpower_is_set = False 00309 step = 0 00310 while not txpower_is_set and (txpower + step >= MIN_TXPOWER \ 00311 or txpower + step <= MAX_TXPOWER): 00312 try: 00313 if txpower + step >= MIN_TXPOWER and txpower + step <= MAX_TXPOWER: 00314 self.set_txpower(txpower + step) 00315 else: 00316 raise Exception() 00317 txpower_is_set = True 00318 except: 00319 step = -step 00320 if step >= 0: 00321 step = step + 1 00322 if txpower_is_set: 00323 self.config['txpower'] = txpower + step 00324 else: 00325 self.config['status'] = 'FAIL' 00326 self.config['errmsg'] = self.config['errmsg'] + " Could not set a valid txpower within the search range" 00327 self.config['txpower'] = self.get_txpower() 00328 00329 # bitrate 00330 if 8 & level: 00331 try: 00332 if config['bitrate'] > 0: 00333 self.set_bitrate(config['bitrate']) 00334 else: 00335 self.set_bitrate(-1, 0) 00336 except Exception as err: 00337 self.config['status'] = 'FAIL' 00338 self.config['errmsg'] = self.config['errmsg'] + "\nCould not set bitrate\n" + str(err) 00339 self.config['bitrate'] = old_bitrate 00340 00341 return self.config 00342 00343 00344 if __name__ == "__main__": 00345 rospy.init_node("ap_hostapd_node") 00346 00347 interface = rospy.get_param("~interface") 00348 args = dict() 00349 try: 00350 args["ip"] = rospy.get_param("~ip") 00351 except KeyError: 00352 pass 00353 try: 00354 args["netmask"] = rospy.get_param("~netmask") 00355 except KeyError: 00356 pass 00357 try: 00358 args["hostapd"] = rospy.get_param("~hostapd_path") 00359 except KeyError: 00360 pass 00361 00362 ap = ApHostapd(interface, **args) 00363 if ap.isrunning(): 00364 ap.stop_ap() 00365 00366 try: 00367 dynamic_reconfigure.server.Server(ApControlConfig, ap.reconfigure) 00368 rospy.spin() 00369 finally: 00370 if ap.isrunning(): 00371 ap.stop_ap()