$search
00001 # -*- coding: latin1 -*- 00002 # Python WiFi -- a library to access wireless card properties via Python 00003 # Copyright (C) 2004 - 2008 Róman Joost 00004 # Copyright (C) 2008 - 2009 Sean Robinson 00005 # 00006 # Contributions from: 00007 # Mike Auty <m.auty@softhome.net> (Iwscanresult, Iwscan) 00008 # 00009 # This library is free software; you can redistribute it and/or 00010 # modify it under the terms of the GNU Lesser General Public License 00011 # as published by the Free Software Foundation; either version 2.1 of 00012 # the License, or (at your option) any later version. 00013 # 00014 # This library is distributed in the hope that it will be useful, but 00015 # WITHOUT ANY WARRANTY; without even the implied warranty of 00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 # Lesser General Public License for more details. 00018 # 00019 # You should have received a copy of the GNU Lesser General Public 00020 # License along with this library; if not, write to the Free Software 00021 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00022 # USA 00023 00024 import struct 00025 import array 00026 import math 00027 import errno 00028 import fcntl 00029 import os 00030 import socket 00031 import time 00032 import re 00033 00034 import flags 00035 from types import StringType, IntType, TupleType 00036 00037 00038 KILO = 10**3 00039 MEGA = 10**6 00040 GIGA = 10**9 00041 00042 00043 def getNICnames(): 00044 """ Extract network device names from /proc/net/dev. 00045 00046 Returns list of device names. Returns empty list if no network 00047 devices are present. 00048 00049 >>> getNICnames() 00050 ['lo', 'eth0'] 00051 00052 """ 00053 device = re.compile('[a-z]{2,}[0-9]*:') 00054 ifnames = [] 00055 00056 fp = open('/proc/net/dev', 'r') 00057 for line in fp: 00058 try: 00059 # append matching pattern, without the trailing colon 00060 ifnames.append(device.search(line).group()[:-1]) 00061 except AttributeError: 00062 pass 00063 return ifnames 00064 00065 def getWNICnames(): 00066 """ Extract wireless device names from /proc/net/wireless. 00067 00068 Returns empty list if no devices are present. 00069 00070 >>> getWNICnames() 00071 ['eth1', 'wifi0'] 00072 00073 """ 00074 device = re.compile('[a-z]{2,}[0-9]*:') 00075 ifnames = [] 00076 00077 fp = open('/proc/net/wireless', 'r') 00078 for line in fp: 00079 try: 00080 # append matching pattern, without the trailing colon 00081 ifnames.append(device.search(line).group()[:-1]) 00082 except AttributeError: 00083 pass 00084 # if we couldn't lookup the devices, try to ask the kernel 00085 if ifnames == []: 00086 ifnames = getConfiguredWNICnames() 00087 00088 return ifnames 00089 00090 def getConfiguredWNICnames(): 00091 """ Get the *configured* ifnames by a systemcall. 00092 00093 >>> getConfiguredWNICnames() 00094 [] 00095 00096 """ 00097 iwstruct = Iwstruct() 00098 ifnames = [] 00099 buff = array.array('c', '\0'*1024) 00100 caddr_t, length = buff.buffer_nfo() 00101 datastr = iwstruct.pack('iP', length, caddr_t) 00102 result = iwstruct._fcntl(flags.SIOCGIFCONF, datastr) 00103 # get the interface names out of the buffer 00104 for i in range(0, 1024, 32): 00105 ifname = buff.tostring()[i:i+32] 00106 ifname = struct.unpack('32s', ifname)[0] 00107 ifname = ifname.split('\0', 1)[0] 00108 if ifname: 00109 # verify if ifnames are really wifi devices 00110 wifi = Wireless(ifname) 00111 try: 00112 result = wifi.getAPaddr() 00113 except IOError, (errno, strerror): 00114 # don't stop on an individual error 00115 pass 00116 if result[0] == 0: 00117 ifnames.append(ifname) 00118 return ifnames 00119 00120 def makedict(**kwargs): 00121 return kwargs 00122 00123 def hex2int(hexstring): 00124 """ Convert hex string to integer. """ 00125 return int(hexstring, 16) 00126 00127 00128 class Wireless(object): 00129 """ Provides high-level access to wireless interfaces. 00130 00131 This class uses WirelessInfo for most access. 00132 00133 """ 00134 00135 def __init__(self, ifname): 00136 self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 00137 self.ifname = ifname 00138 self.iwstruct = Iwstruct() 00139 self.wireless_info = WirelessInfo(self.ifname) 00140 00141 def getAPaddr(self): 00142 """ Returns the access point MAC address. 00143 00144 >>> from iwlibs import Wireless, getNICnames 00145 >>> ifnames = getNICnames() 00146 >>> ifnames 00147 ['eth1', 'wifi0'] 00148 >>> wifi = Wireless(ifnames[0]) 00149 >>> wifi.getAPaddr() 00150 '00:0D:88:8E:4E:93' 00151 00152 Test with non-wifi card: 00153 >>> wifi = Wireless('eth0') 00154 >>> wifi.getAPaddr() 00155 (95, 'Operation not supported') 00156 00157 Test with non-existant card: 00158 >>> wifi = Wireless('eth2') 00159 >>> wifi.getAPaddr() 00160 (19, 'No such device') 00161 00162 """ 00163 return self.wireless_info.getAPaddr() 00164 00165 def setAPaddr(self, addr): 00166 """ Sets the access point MAC address. 00167 00168 translated from iwconfig.c 00169 00170 """ 00171 addr = addr.upper() 00172 if (addr == "AUTO" or addr == "ANY"): 00173 mac_addr = "\xFF"*flags.ETH_ALEN 00174 elif addr == "OFF": 00175 mac_addr = '\x00'*flags.ETH_ALEN 00176 else: 00177 if ":" not in addr: return (errno.ENOSYS, os.strerror(errno.ENOSYS)) 00178 mac_addr = "%c%c%c%c%c%c" % tuple(map(hex2int, addr.split(':'))) 00179 00180 iwreq = self.iwstruct.pack("H14s", 1, mac_addr) 00181 status, result = self.iwstruct.iw_set_ext(self.ifname, 00182 flags.SIOCSIWAP, 00183 iwreq) 00184 00185 def _formatBitrate(self, raw_bitrate): 00186 """ Returns formatted bitrate. 00187 00188 'raw_bitrate' -- long -- The unformatted bitrate as a long integer. 00189 00190 """ 00191 if raw_bitrate >= GIGA: 00192 return "%g Gb/s" % (float(raw_bitrate)/GIGA) 00193 if raw_bitrate >= MEGA: 00194 return "%g Mb/s" % (float(raw_bitrate)/MEGA) 00195 if raw_bitrate >= KILO: 00196 return "%g kb/s" % (float(raw_bitrate)/KILO) 00197 00198 def getBitrate(self): 00199 """ Returns the device's currently set bit rate in Mbit. 00200 00201 >>> from iwlibs import Wireless 00202 >>> wifi = Wireless('eth1') 00203 >>> wifi.getBitrate() 00204 '11 Mb/s' 00205 00206 """ 00207 iwparam = self.wireless_info.getBitrate() 00208 return self._formatBitrate(iwparam.value) 00209 00210 def getBitrates(self): 00211 """ Returns the number of bitrates available for the device. 00212 00213 >>> from iwlibs import Wireless 00214 >>> wifi = Wireless('eth1') 00215 >>> num, rates = wifi.getBitrates() 00216 >>> num == len(rates) 00217 True 00218 00219 """ 00220 num_bitrates, bitrates = self.wireless_info.getBitrates() 00221 cooked_rates = [] 00222 for rate in bitrates: 00223 cooked_rates.append(self._formatBitrate(rate)) 00224 return (num_bitrates, cooked_rates) 00225 00226 def _formatFrequency(self, raw_frequency): 00227 """ Returns formatted frequency. 00228 00229 'raw_frequency' -- long -- The unformatted frequency as a long 00230 integer. 00231 00232 """ 00233 raw_frequency = float(raw_frequency) 00234 if raw_frequency >= GIGA: 00235 return "%0.3f GHz" % (raw_frequency/GIGA) 00236 if raw_frequency >= MEGA: 00237 return "%0.3f MHZ" % (raw_frequency/MEGA) 00238 if raw_frequency >= KILO: 00239 return "%0.3f kHz" % (raw_frequency/KILO) 00240 # This is probably a channel number 00241 raw_frequency = int(raw_frequency) 00242 try: 00243 return self.getChannelInfo()[1][raw_frequency-1] 00244 except IndexError: 00245 # probably auto (i.e. -1 (a.k.a. 255)) 00246 pass 00247 return raw_frequency 00248 00249 def getChannelInfo(self): 00250 """ Returns the number of channels and available frequencies for 00251 the device. 00252 00253 >>> from iwlibs import Wireless 00254 >>> wifi = Wireless('eth1') 00255 >>> num, rates = wifi.getChannelInfo() 00256 >>> num == len(rates) 00257 True 00258 00259 """ 00260 iwrange = Iwrange(self.ifname) 00261 frequencies = [] 00262 for freq in iwrange.frequencies: 00263 frequencies.append(self._formatFrequency(freq)) 00264 return (iwrange.num_channels, frequencies) 00265 00266 def getEssid(self): 00267 """ Returns the current ESSID information. 00268 00269 >>> from iwlibs import Wireless 00270 >>> wifi = Wireless('eth1') 00271 >>> wifi.getEssid() 00272 'romanofski' 00273 00274 """ 00275 return self.wireless_info.getEssid() 00276 00277 def setEssid(self, essid): 00278 """ Sets the ESSID. 00279 00280 >>> from iwlibs import Wireless 00281 >>> wifi = Wireless('eth1') 00282 >>> wifi.getEssid() 00283 'romanofski' 00284 >>> wifi.setEssid('Joost') 00285 >>> wifi.getEssid() 00286 'Joost' 00287 00288 """ 00289 if len(essid) > flags.IW_ESSID_MAX_SIZE: 00290 raise OverflowError(errno.EOVERFLOW, os.strerror(errno.EOVERFLOW)) 00291 iwpoint = Iwpoint(essid, 1) 00292 status, result = self.iwstruct.iw_set_ext(self.ifname, 00293 flags.SIOCSIWESSID, 00294 data=iwpoint.packed_data) 00295 00296 def getEncryption(self): 00297 """ Get the association mode, which is probably a string of '*', 00298 'open', 'private', 'off'. 00299 00300 As a normal user, you will get an 'Operation not permitted' 00301 error: 00302 00303 >>> from iwlibs import Wireless 00304 >>> wifi = Wireless('eth1') 00305 >>> wifi.getEncryption() 00306 (1, 'Operation not permitted') 00307 00308 """ 00309 # use an IW_ENCODING_TOKEN_MAX-cell array of NULLs 00310 # as space for ioctl to write encryption info 00311 iwpoint = Iwpoint('\x00'*flags.IW_ENCODING_TOKEN_MAX) 00312 status, result = self.iwstruct.iw_get_ext(self.ifname, 00313 flags.SIOCGIWENCODE, 00314 data=iwpoint.packed_data) 00315 iwpoint.update(result) 00316 00317 if iwpoint.flags & flags.IW_ENCODE_NOKEY > 0: 00318 return '**'*iwpoint.length 00319 elif iwpoint.flags & flags.IW_ENCODE_OPEN > 0: 00320 return 'open' 00321 elif iwpoint.flags & flags.IW_ENCODE_RESTRICTED > 0: 00322 return 'restricted' 00323 elif iwpoint.flags & flags.IW_ENCODE_DISABLED > 0: 00324 return 'off' 00325 00326 def setEncryption(self, mode): 00327 """ Set the association mode. 00328 00329 As a normal user, you will get an 'Operation not permitted' 00330 error: 00331 00332 >>> from iwlibs import Wireless 00333 >>> wifi = Wireless('eth1') 00334 >>> wifi.setEncryption() 00335 (1, 'Operation not permitted') 00336 00337 """ 00338 if type(mode) == IntType: 00339 mode = mode 00340 else: 00341 mode = mode.upper() 00342 numeric_mode = self.getEncryption(symbolic=False) 00343 # turn off all associate modes, but do not touch other flag bits 00344 numeric_mode = numeric_mode & ~flags.IW_ENCODE_OPEN \ 00345 & ~flags.IW_ENCODE_RESTRICTED \ 00346 & ~flags.IW_ENCODE_DISABLED 00347 if (mode == 'OPEN') or (mode == flags.IW_ENCODE_OPEN): 00348 numeric_mode = numeric_mode | flags.IW_ENCODE_OPEN 00349 elif (mode == 'RESTRICTED') or (mode == flags.IW_ENCODE_RESTRICTED): 00350 numeric_mode = numeric_mode | flags.IW_ENCODE_RESTRICTED 00351 elif (mode == 'OFF') or (mode == flags.IW_ENCODE_DISABLED): 00352 numeric_mode = numeric_mode | flags.IW_ENCODE_DISABLED 00353 iwpoint = Iwpoint('\x00'*flags.IW_ENCODING_TOKEN_MAX, numeric_mode) 00354 status, result = self.iwstruct.iw_get_ext(self.ifname, 00355 flags.SIOCSIWENCODE, 00356 data=iwpoint.packed_data) 00357 00358 def getKey(self, key=0, formatted=True): 00359 """ Get an encryption key. 00360 00361 key 0 is current key, otherwise, retrieve specific key (1-4) 00362 00363 As a normal user, you will get an 'Operation not permitted' 00364 error: 00365 00366 >>> from iwlibs import Wireless 00367 >>> wifi = Wireless('eth1') 00368 >>> wifi.getKey() 00369 ABCD-9512-34 00370 00371 """ 00372 iwpoint = self.wireless_info.getKey(key) 00373 00374 # build a list of each char in key 00375 raw_key = map(ord, iwpoint.buff.tolist())[:iwpoint.length] 00376 if sum(raw_key) == 0: 00377 return "off" 00378 if not formatted: 00379 return raw_key 00380 00381 # format key in standard form 00382 key = "%.2X" % raw_key[0] 00383 for i in range(1, iwpoint.length): 00384 if ( i & 0x1 ) == 0: 00385 key = key + '-' 00386 key = key + "%.2X" % raw_key[i] 00387 return key 00388 00389 def setKey(self, key, index=0): 00390 """ Set an encryption key. 00391 00392 As a normal user, you will get an 'Operation not permitted' 00393 error: 00394 00395 >>> from iwlibs import Wireless 00396 >>> wifi = Wireless('eth1') 00397 >>> wifi.setKey() 00398 00399 """ 00400 if index not in range(1, flags.IW_ENCODE_INDEX): 00401 raise IndexError 00402 00403 if key: 00404 cooked_key = '' 00405 for i in range(0, len(key), 2): 00406 cooked_key = cooked_key + chr(hex2int(key[i:i+2])) 00407 else: 00408 raw_key = self.getKey(index, False) 00409 cooked_key = map(chr, raw_key) 00410 00411 iwpoint = Iwpoint(cooked_key, 00412 index + flags.IW_ENCODE_ENABLED) 00413 status, result = self.iwstruct.iw_get_ext(self.ifname, 00414 flags.SIOCSIWENCODE, 00415 data=iwpoint.packed_data) 00416 00417 def getKeys(self): 00418 """ Get all encryption keys. 00419 00420 Returns a list of tuples. 00421 00422 As a normal user, you will get a 'Operation not permitted' 00423 error: 00424 00425 >>> from iwlibs import Wireless 00426 >>> wifi = Wireless('eth1') 00427 >>> wifi.getKeys() 00428 [(1, '1234-5678-91'), (2, None), (3, 'ABCD-EFAB-CD'), (4, None)] 00429 00430 """ 00431 iwrange = Iwrange(self.ifname); 00432 keys = [] 00433 if iwrange.max_encoding_tokens > 0: 00434 for i in range(1, iwrange.max_encoding_tokens+1): 00435 keys.append((i, self.getKey(i))) 00436 return keys 00437 00438 def getFragmentation(self): 00439 """ Returns the fragmentation threshold. 00440 00441 It depends on what the driver says. If you have fragmentation 00442 threshold turned on, you'll get an int. If it's turned off 00443 you'll get a string: 'off'. 00444 00445 >>> from iwlibs import Wireless 00446 >>> wifi = Wireless('eth1') 00447 >>> wifi.getFragmentation() 00448 'off' 00449 00450 """ 00451 iwparam = self.wireless_info.getFragmentation() 00452 if iwparam.disabled: 00453 return "off" 00454 return iwparam.value 00455 00456 def getFrequency(self): 00457 """ Returns currently set frequency of the card. 00458 00459 >>> from iwlibs import Wireless 00460 >>> wifi = Wireless('eth1') 00461 >>> wifi.getFrequency() 00462 '2.417 GHz' 00463 00464 """ 00465 iwfreq = self.wireless_info.getFrequency() 00466 return self._formatFrequency(iwfreq.getFrequency()) 00467 00468 def setFrequency(self, freq): 00469 """ Sets the frequency on the card. 00470 00471 translated from iwconfig.c 00472 00473 """ 00474 iwstruct = Iwstruct() 00475 if freq == "auto": 00476 iwreq = iwstruct.pack("ihBB", -1, 0, 0, flags.IW_FREQ_AUTO) 00477 else: 00478 if freq == "fixed": 00479 freq = self.getFrequency() 00480 freq_pattern = re.compile("([\d\.]+)(\w)", re.I|re.M|re.S) 00481 freq_match = freq_pattern.search(freq) 00482 freq_num, unit = freq_match.groups() 00483 if unit == "G": freq_num = float(freq_num) * GIGA 00484 if unit == "M": freq_num = float(freq_num) * MEGA 00485 if unit == "k": freq_num = float(freq_num) * KILO 00486 e = math.floor(math.log10(freq_num)) 00487 if e > 8: 00488 m = int(math.floor(freq_num / math.pow(10, e - 6))) * 100 00489 e = e - 8 00490 else: 00491 m = int(math.floor(freq_num)) 00492 e = 0 00493 iwreq = iwstruct.pack("ihBB", m, e, 0, flags.IW_FREQ_FIXED) 00494 status, result = iwstruct.iw_set_ext(self.ifname, 00495 flags.SIOCSIWFREQ, 00496 iwreq) 00497 00498 def getMode(self): 00499 """ Returns currently set operation mode. 00500 00501 >>> from iwlibs import Wireless 00502 >>> wifi = Wireless('eth1') 00503 >>> wifi.getMode() 00504 'Managed' 00505 00506 """ 00507 return flags.modes[self.wireless_info.getMode()] 00508 00509 def setMode(self, mode): 00510 """ Sets the operation mode. 00511 00512 """ 00513 try: 00514 this_modes = [x.lower() for x in flags.modes] 00515 mode = mode.lower() 00516 wifimode = this_modes.index(mode) 00517 except ValueError: 00518 raise ValueError("Invalid operation mode!") 00519 00520 datastr = self.iwstruct.pack('I', wifimode) 00521 status, result = self.iwstruct.iw_set_ext(self.ifname, 00522 flags.SIOCSIWMODE, 00523 data=datastr) 00524 00525 def getWirelessName(self): 00526 """ Returns the wireless name. 00527 00528 >>> from iwlibs import Wireless 00529 >>> wifi = Wireless('eth1') 00530 >>> wifi.getWirelessName() 00531 'IEEE 802.11-DS' 00532 00533 """ 00534 return self.wireless_info.getWirelessName() 00535 00536 def getPowermanagement(self): 00537 """ Returns the power management settings. 00538 00539 #>>> from iwlibs import Wireless 00540 #>>> wifi = Wireless('eth1') 00541 #>>> wifi.getPowermanagement() 00542 #'off' 00543 00544 """ 00545 iwrange = Iwrange(self.ifname) 00546 iwparam = self.wireless_info.getPower() 00547 return (iwrange.pm_capa, 00548 (iwrange.pmp_flags, iwrange.min_pmp, iwrange.max_pmp), 00549 (iwrange.pmt_flags, iwrange.min_pmt, iwrange.max_pmt), 00550 (iwrange.pms_flags, iwrange.min_pms, iwrange.max_pms), 00551 iwparam) 00552 00553 def getQualityMax(self): 00554 """ Returns an Iwquality object with maximum quality information. 00555 00556 >>> from iwlibs import Wireless 00557 >>> wifi = Wireless('eth1') 00558 >>> mq = wifi.getQualityMax() 00559 >>> print "quality:", mq.quality, "signal:", mq.siglevel, "noise:", mq.nlevel 00560 quality: 38 signal: 13 noise: 0 00561 00562 """ 00563 iwrange = Iwrange(self.ifname) 00564 if iwrange.errorflag: 00565 return (iwrange.errorflag, iwrange.error) 00566 return iwrange.max_qual 00567 00568 def getQualityAvg(self): 00569 """ Returns an Iwquality object with average quality information. 00570 00571 >>> from iwlibs import Wireless 00572 >>> wifi = Wireless('eth1') 00573 >>> aq = wifi.getQualityAvg() 00574 >>> print "quality:", aq.quality, "signal:", aq.siglevel, "noise:", aq.nlevel 00575 quality: 38 signal: 13 noise: 0 00576 00577 """ 00578 iwrange = Iwrange(self.ifname) 00579 if iwrange.errorflag: 00580 return (iwrange.errorflag, iwrange.error) 00581 return iwrange.avg_qual 00582 00583 def getRetrylimit(self): 00584 """ Returns the retry/lifetime limit. 00585 00586 man iwconfig: 00587 "Most cards have MAC retransmissions, and some allow to set 00588 the behaviour of the retry mechanism." 00589 00590 >>> from iwlibs import Wireless 00591 >>> wifi = Wireless('eth1') 00592 >>> wifi.getRetrylimit() 00593 16 00594 00595 """ 00596 iwparam = self.wireless_info.getRetry() 00597 return iwparam.value 00598 00599 def getRTS(self): 00600 """ Returns the RTS threshold, likely to be int, 'auto', 00601 'fixed', 'off' 00602 00603 man iwconfig: 00604 "RTS/CTS adds a handshake before each packet transmission to 00605 make sure that the channel is clear. This adds overhead, but 00606 increases performance in case of hidden nodes or a large 00607 number of active nodes. This parameter sets the size of the 00608 smallest packet for which the node sends RTS; a value equal 00609 to the maximum packet size disable the mechanism." 00610 00611 >>> from iwlibs import Wireless 00612 >>> wifi = Wireless('eth1') 00613 >>> wifi.getRTS() 00614 'off' 00615 00616 """ 00617 iwparam = self.wireless_info.getRTS() 00618 if iwparam.disabled: 00619 return "off" 00620 return iwparam.value 00621 00622 def getSensitivity(self): 00623 """ Returns sensitivity information. 00624 00625 man iwconfig: 00626 "This is the lowest signal level for which the hardware 00627 attempt packet reception, signals weaker than this are 00628 ignored. This is used to avoid receiving background noise, 00629 so you should set it according to the average noise 00630 level. Positive values are assumed to be the raw value used 00631 by the hardware or a percentage, negative values are 00632 assumed to be dBm." 00633 00634 >>> from iwlibs import Wireless 00635 >>> wifi = Wireless('eth1') 00636 >>> wifi.getSensitivity() 00637 'off' 00638 00639 """ 00640 iwparam = self.wireless_info.getSensitivity() 00641 return iwparam.value 00642 00643 def getTXPower(self): 00644 """ Returns the transmit power in dBm. 00645 00646 >>> from iwlibs import Wireless 00647 >>> wifi = Wireless('eth1') 00648 >>> wifi.getTXPower() 00649 '17 dBm' 00650 00651 """ 00652 def mw2dbm(self, mwatt): 00653 """ Converts mW to dBm (float). """ 00654 if mwatt == 0: 00655 return 0 00656 return math.ceil(10.0 * math.log10(mwatt)) 00657 00658 iwparam = self.wireless_info.getTXPower() 00659 return "%i dBm" % iwparam.value 00660 00661 def getStatistics(self): 00662 """ Returns statistics information which can also be found in 00663 /proc/net/wireless. 00664 00665 """ 00666 iwstats = Iwstats(self.ifname) 00667 if iwstats.errorflag > 0: 00668 return (iwstats.errorflag, iwstats.error) 00669 return [iwstats.status, iwstats.qual, iwstats.discard, 00670 iwstats.missed_beacon] 00671 00672 def scan(self): 00673 """ Returns Iwscanresult objects, after a successful scan. """ 00674 return Iwscan(self.ifname) 00675 00676 def commit(self): 00677 """ Commit pending changes. """ 00678 status, result = self.iwstruct.iw_set_ext(self.ifname, 00679 flags.SIOCSIWCOMMIT) 00680 return (status, result) 00681 00682 00683 class WirelessConfig(object): 00684 """ Low level access to wireless information on a device. This class 00685 contains only those things absolutely needed to configure a card. 00686 00687 WirelessConfig implements the wireless_config struct in iwlib.h. 00688 It will probably never be called directly, but instead be used via 00689 WirelessInfo. 00690 00691 """ 00692 00693 def __init__(self, ifname): 00694 self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 00695 self.ifname = ifname 00696 self.iwstruct = Iwstruct() 00697 #self.nwid = Iwparam 00698 self.freq_flags = 0 00699 self.essid_on = 0 00700 00701 def getWirelessName(self): 00702 """ Returns the wireless name. 00703 00704 >>> from iwlibs import Wireless 00705 >>> wifi = Wireless('eth1') 00706 >>> wifi.getWirelessName() 00707 'IEEE 802.11-DS' 00708 00709 """ 00710 status, result = self.iwstruct.iw_get_ext(self.ifname, 00711 flags.SIOCGIWNAME) 00712 return result.tostring().strip('\x00') 00713 00714 def getEncryption(self): 00715 """ Returns the encryption status. 00716 00717 >>> from iwlibs import Wireless 00718 >>> wifi = Wireless('eth1') 00719 >>> wifi.getEncryption() 00720 'off' 00721 00722 """ 00723 # use an IW_ENCODING_TOKEN_MAX-cell array of NULLs 00724 # as space for ioctl to write encryption info 00725 iwpoint = Iwpoint('\x00'*flags.IW_ENCODING_TOKEN_MAX) 00726 status, result = self.iwstruct.iw_get_ext(self.ifname, 00727 flags.SIOCGIWENCODE, 00728 data=iwpoint.packed_data) 00729 iwpoint.update(result) 00730 00731 return iwpoint 00732 00733 def getFrequency(self): 00734 """ Returns currently set frequency of the card. 00735 00736 >>> from iwlibs import Wireless 00737 >>> wifi = Wireless('eth1') 00738 >>> wifi.getFrequency() 00739 '2.417 GHz' 00740 00741 """ 00742 status, result = self.iwstruct.iw_get_ext(self.ifname, 00743 flags.SIOCGIWFREQ) 00744 return Iwfreq(result) 00745 00746 def getKey(self, key=0): 00747 """ Get an encryption key. 00748 00749 key 0 is current key, otherwise, retrieve specific key (1-4) 00750 00751 As a normal user, you will get an 'Operation not permitted' 00752 error: 00753 00754 >>> from iwlibs import Wireless 00755 >>> wifi_conf = WirelessConfig('eth1') 00756 >>> isinstance(wifi_conf.getKey(), Iwpoint) 00757 True 00758 00759 """ 00760 # use an IW_ENCODING_TOKEN_MAX-cell array of NULLs 00761 # as space for ioctl to write encryption info 00762 iwpoint = Iwpoint('\x00'*flags.IW_ENCODING_TOKEN_MAX, key) 00763 status, result = self.iwstruct.iw_get_ext(self.ifname, 00764 flags.SIOCGIWENCODE, 00765 data=iwpoint.packed_data) 00766 iwpoint.update(result) 00767 return iwpoint 00768 00769 def getEssid(self): 00770 """ Returns the current ESSID information. 00771 00772 >>> from iwlibs import Wireless 00773 >>> wifi = Wireless('eth1') 00774 >>> wifi.getEssid() 00775 'romanofski' 00776 00777 """ 00778 # use an IW_ESSID_MAX_SIZE-cell array of NULLs 00779 # as space for ioctl to write ESSID 00780 iwpoint = Iwpoint('\x00'*flags.IW_ESSID_MAX_SIZE) 00781 status, result = self.iwstruct.iw_get_ext(self.ifname, 00782 flags.SIOCGIWESSID, 00783 data=iwpoint.packed_data) 00784 raw_essid = iwpoint.buff.tostring() 00785 return raw_essid.strip('\x00') 00786 00787 def getMode(self): 00788 """ Returns currently set operation mode. 00789 00790 >>> from iwlibs import Wireless 00791 >>> wifi = Wireless('eth1') 00792 >>> wifi.getMode() 00793 'Managed' 00794 00795 """ 00796 status, result = self.iwstruct.iw_get_ext(self.ifname, 00797 flags.SIOCGIWMODE) 00798 return self.iwstruct.unpack('I', result[:4])[0] 00799 00800 00801 class WirelessInfo(WirelessConfig): 00802 """ Low level access to wireless extensions on a device. This class 00803 is the exhaustive list of information for a card. 00804 00805 WirelessInfo implements the wireless_info struct in iwlib.h. 00806 This class should be used by those needing lower-level access 00807 than Wireless provides. 00808 00809 """ 00810 00811 def __init__(self, ifname): 00812 self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 00813 self.ifname = ifname 00814 self.iwstruct = Iwstruct() 00815 00816 self.nickname = "" 00817 # Stats 00818 self.stats = Iwstats 00819 self.range = Iwrange 00820 # Auth params for WPA/802.1x/802.11i 00821 self.auth_key_mgmt = 0 00822 self.has_auth_key_mgmt = 0 00823 self.auth_cipher_pairwise = 0 00824 self.has_auth_cipher_pairwise = 0 00825 self.auth_cipher_group = 0 00826 self.has_auth_cipher_group = 0 00827 WirelessConfig.__init__(self, ifname) 00828 00829 def getSensitivity(self): 00830 """ Returns sensitivity information. 00831 00832 man iwconfig: 00833 "This is the lowest signal level for which the hardware 00834 attempt packet reception, signals weaker than this are 00835 ignored. This is used to avoid receiving background noise, 00836 so you should set it according to the average noise 00837 level. Positive values are assumed to be the raw value used 00838 by the hardware or a percentage, negative values are 00839 assumed to be dBm." 00840 00841 >>> from iwlibs import Wireless 00842 >>> wifi = Wireless('eth1') 00843 >>> wifi.getSensitivity() 00844 'off' 00845 00846 """ 00847 return Iwparam(self.ifname, flags.SIOCGIWSENS) 00848 00849 def getAPaddr(self): 00850 """ Returns the access point MAC address. 00851 00852 >>> from iwlibs import Wireless, getNICnames 00853 >>> ifnames = getNICnames() 00854 >>> ifnames 00855 ['eth1', 'wifi0'] 00856 >>> wifi = Wireless(ifnames[0]) 00857 >>> wifi.getAPaddr() 00858 '00:0D:88:8E:4E:93' 00859 00860 Test with non-wifi card: 00861 >>> wifi = Wireless('eth0') 00862 >>> wifi.getAPaddr() 00863 (95, 'Operation not supported') 00864 00865 Test with non-existant card: 00866 >>> wifi = Wireless('eth2') 00867 >>> wifi.getAPaddr() 00868 (19, 'No such device') 00869 00870 """ 00871 buff, datastr = self.iwstruct.pack_wrq(32) 00872 status, result = self.iwstruct.iw_get_ext(self.ifname, 00873 flags.SIOCGIWAP, 00874 data=datastr) 00875 # Extracts MAC address from packed data and returns it as a str. 00876 mac_addr = struct.unpack('xxBBBBBB', result[:8]) 00877 return "%02X:%02X:%02X:%02X:%02X:%02X" % mac_addr 00878 00879 def getBitrate(self): 00880 """ Returns the device's currently set bit rate. 00881 00882 >>> from iwlibs import Wireless 00883 >>> wifi = Wireless('eth1') 00884 >>> wifi.getBitrate() 00885 '11 Mb/s' 00886 00887 """ 00888 return Iwparam(self.ifname, flags.SIOCGIWRATE) 00889 00890 def getBitrates(self): 00891 """ Returns the device's number and list of available bit rates. 00892 00893 The bit rates in the list are long integer type. 00894 00895 """ 00896 iwrange = Iwrange(self.ifname) 00897 return (iwrange.num_bitrates, iwrange.bitrates) 00898 00899 def getRTS(self): 00900 """ Returns the RTS threshold. 00901 00902 >>> from iwlibs import Wireless 00903 >>> wifi = Wireless('eth1') 00904 >>> wifi.getRTS() 00905 'off' 00906 00907 """ 00908 return Iwparam(self.ifname, flags.SIOCGIWRTS) 00909 00910 def getFragmentation(self): 00911 """ Returns the fragmentation threshold. 00912 00913 >>> from iwlibs import Wireless 00914 >>> wifi = Wireless('eth1') 00915 >>> wifi.getFragmentation() 00916 'off' 00917 00918 """ 00919 return Iwparam(self.ifname, flags.SIOCGIWFRAG) 00920 00921 def getPower(self): 00922 """ Returns the power management settings. 00923 00924 #>>> from iwlibs import Wireless 00925 #>>> wifi = Wireless('eth1') 00926 #>>> wifi.getPowermanagement() 00927 #'off' 00928 00929 """ 00930 return Iwparam(self.ifname, flags.SIOCGIWPOWER) 00931 00932 def getTXPower(self): 00933 """ Returns the transmit power in dBm. 00934 00935 >>> from iwlibs import Wireless 00936 >>> wifi = Wireless('eth1') 00937 >>> wifi.getTXPower() 00938 '17 dBm' 00939 00940 """ 00941 return Iwparam(self.ifname, flags.SIOCGIWTXPOW) 00942 00943 def getRetry(self): 00944 """ Returns the retry/lifetime limit. 00945 00946 man iwconfig: 00947 "Most cards have MAC retransmissions, and some allow to set 00948 the behaviour of the retry mechanism." 00949 00950 >>> from iwlibs import Wireless 00951 >>> wifi = Wireless('eth1') 00952 >>> wifi.getRetrylimit() 00953 16 00954 00955 """ 00956 return Iwparam(self.ifname, flags.SIOCGIWRETRY) 00957 00958 00959 class Iwstruct(object): 00960 """ The basic class to handle iwstruct data. """ 00961 00962 def __init__(self): 00963 self.idx = 0 00964 self.sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 00965 00966 def parse_data(self, fmt, data): 00967 """ Unpacks raw C data. """ 00968 size = struct.calcsize(fmt) 00969 idx = self.idx 00970 00971 datastr = data[idx:idx + size] 00972 self.idx = idx+size 00973 value = struct.unpack(fmt, datastr) 00974 00975 # take care of a tuple like (int, ) 00976 if len(value) == 1: 00977 return value[0] 00978 else: 00979 return value 00980 00981 def pack(self, fmt, *args): 00982 """ Calls struct.pack and returns the result. """ 00983 return struct.pack(fmt, *args) 00984 00985 def pack_wrq(self, buffsize): 00986 """ Packs wireless request data for sending it to the kernel. """ 00987 # Prepare a buffer 00988 # We need the address of our buffer and the size for it. The 00989 # ioctl itself looks for the pointer to the address in our 00990 # memory and the size of it. 00991 # Don't change the order how the structure is packed!!! 00992 buff = array.array('c', '\0'*buffsize) 00993 caddr_t, length = buff.buffer_info() 00994 datastr = struct.pack('Pi', caddr_t, length) 00995 return buff, datastr 00996 00997 def pack_test(self, string, buffsize): 00998 """ Packs wireless request data for sending it to the kernel. """ 00999 buffsize = buffsize - len(string) 01000 buff = array.array('c', string+'\0'*buffsize) 01001 caddr_t, length = buff.buffer_info() 01002 s = struct.pack('PHH', caddr_t, length, 1) 01003 return buff, s 01004 01005 def unpack(self, fmt, packed_data): 01006 """ Unpacks data with given format. """ 01007 return struct.unpack(fmt, packed_data) 01008 01009 def _fcntl(self, request, args): 01010 return fcntl.ioctl(self.sockfd.fileno(), request, args) 01011 01012 def iw_get_ext(self, ifname, request, data=None): 01013 """ Read information from ifname. """ 01014 buff = flags.IFNAMSIZE-len(ifname) 01015 ifreq = array.array('c', ifname + '\0'*buff) 01016 # put some additional data behind the interface name 01017 if data is not None: 01018 ifreq.extend(data) 01019 else: 01020 buff = 32 # - flags.IFNAMSIZE 01021 ifreq.extend('\0'*buff) 01022 01023 result = self._fcntl(request, ifreq) 01024 return (result, ifreq[flags.IFNAMSIZE:]) 01025 01026 def iw_set_ext(self, ifname, operation, data=None): 01027 """ Set options on ifname. """ 01028 return self.iw_get_ext(ifname, operation, data) 01029 01030 #def getMAC(self, packed_data): 01031 #""" Extracts MAC address from packed data and returns it as a str. """ 01032 #mac_addr = struct.unpack('xxBBBBBB', packed_data[:8]) 01033 #return "%02X:%02X:%02X:%02X:%02X:%02X" % mac_addr 01034 01035 01036 class Iwparam(object): 01037 """ Class to hold iwparam data. """ 01038 01039 def __init__(self, ifname, ioctl): 01040 # (i) value, (b) fixed, (b) disabled, (H) flags 01041 self.fmt = "ibbH" 01042 self.ifname = ifname 01043 self.ioctl = ioctl 01044 self.value = 0 01045 self.fixed = 0 01046 self.disabled = 0 01047 self.flags = 0 01048 self.update() 01049 01050 def update(self): 01051 """ Updates Iwstruct object by a system call to the kernel 01052 and updates internal attributes. 01053 01054 """ 01055 iwstruct = Iwstruct() 01056 status, result = iwstruct.iw_get_ext(self.ifname, self.ioctl) 01057 self._parse(result) 01058 01059 def _parse(self, data): 01060 """ Unpacks iwparam data. """ 01061 iwstruct = Iwstruct() 01062 self.value, self.fixed, self.disabled, self.flags = \ 01063 iwstruct.parse_data(self.fmt, data) 01064 01065 class Iwfreq(object): 01066 """ Class to hold iwfreq data. """ 01067 01068 def __init__(self, data=None): 01069 # (i) mantissa, (h) exponent, (b) list index, (b) flags 01070 self.fmt = "ihbb" 01071 self.m = 0 01072 self.e = 0 01073 self.index = 0 01074 self.flags = 0 01075 if data: 01076 if isinstance(data, TupleType): 01077 self.m, self.e, self.index, self.flags = data 01078 else: 01079 self.parse(data) 01080 01081 def parse(self, data): 01082 """ Unpacks iw_freq. """ 01083 size = struct.calcsize(self.fmt) 01084 self.m, self.e, self.index, self.flags = struct.unpack(self.fmt, data[:size]) 01085 01086 def getFrequency(self): 01087 """ Returns frequency or channel, depending on the driver. """ 01088 if self.e == 0: 01089 return self.m 01090 else: 01091 return self.m*10**self.e 01092 01093 def setFrequency(self, value): 01094 """ Sets mantissa and exponent from given frequency (or channel). """ 01095 if value % GIGA == 0: 01096 self.e = 9 01097 if value % MEGA == 0: 01098 self.e = 6 01099 if value % KILO == 0: 01100 self.e = 3 01101 else: 01102 self.e = 0 01103 self.m = value / 10**self.e 01104 01105 01106 class Iwstats(object): 01107 """ Class to hold iwstat data. """ 01108 01109 def __init__(self, ifname): 01110 # (2B) status, 4B iw_quality, 6i iw_discarded 01111 self.fmt = "2B4B6i" 01112 self.status = 0 01113 self.qual = Iwquality() 01114 self.discard = {} 01115 self.missed_beacon = 0 01116 self.ifname = ifname 01117 self.errorflag = 0 01118 self.error = "" 01119 self.update() 01120 01121 def update(self): 01122 """ Updates Iwstats object by a system call to the kernel 01123 and updates internal attributes. 01124 01125 """ 01126 iwstruct = Iwstruct() 01127 buff, s = iwstruct.pack_wrq(32) 01128 i, result = iwstruct.iw_get_ext(self.ifname, 01129 flags.SIOCGIWSTATS, 01130 data=s) 01131 if i > 0: 01132 self.error = result 01133 self.errorflag = i 01134 self._parse(buff.tostring()) 01135 01136 def _parse(self, data): 01137 """ Unpacks iwstruct data. """ 01138 iwstruct = Iwstruct() 01139 iwqual = Iwquality() 01140 iwstats_data = iwstruct.parse_data(self.fmt, data) 01141 01142 self.status = iwstats_data[0:2] 01143 self.qual.quality, self.qual.siglevel, self.qual.nlevel, \ 01144 self.qual.updated = iwstats_data[2:6] 01145 # Conversion below works for ralink. Many other cases aren't handled properly by this code. We should be checking flags to do this properly. 01146 self.qual.siglevel -= 256 01147 self.qual.nlevel -= 256 01148 nwid, code, frag, retries, iwflags = iwstats_data[6:11] 01149 self.missed_beacon = iwstats_data[11:12][0] 01150 self.discard = makedict(nwid=nwid, code=code, 01151 fragment=frag, retries=retries, misc=iwflags) 01152 01153 01154 class Iwquality(object): 01155 """ Class to hold iwquality data. """ 01156 01157 def __init__(self): 01158 self.quality = 0 01159 self.siglevel = 0 01160 self.nlevel = 0 01161 self.updated = 0 01162 self.fmt = "4B" 01163 01164 def parse(self, data): 01165 """ Unpacks iwquality data. """ 01166 iwstruct = Iwstruct() 01167 qual, siglevel, nlevel, iwflags = iwstruct.parse_data(self.fmt, data) 01168 01169 # compute signal and noise level 01170 self.siglevel = siglevel 01171 self.nlevel = nlevel 01172 01173 # asign the other values 01174 self.quality = qual 01175 self.updated = iwflags 01176 01177 def setValues(self, vallist): 01178 """ Assigns values given by a list to our attributes. """ 01179 attributes = ["quality", "siglevel", "nlevel", "updated"] 01180 assert len(vallist) == 4 01181 01182 for i in range(len(vallist)): 01183 setattr(self, attributes[i], vallist[i]) 01184 01185 def getSignallevel(self): 01186 """ Returns signal level. """ 01187 return self.siglevel 01188 01189 def setSignallevel(self, siglevel): 01190 """ Sets signal level. """ 01191 self.siglevel = siglevel 01192 signallevel = property(getSignallevel, setSignallevel) 01193 01194 def getNoiselevel(self): 01195 """ Returns noise level. """ 01196 return self.nlevel 01197 01198 def setNoiselevel(self, val): 01199 # currently not implemented 01200 # XXX 01201 self.nlevel = val 01202 noiselevel = property(getNoiselevel, setNoiselevel) 01203 01204 01205 class Iwpoint(object): 01206 """ Class to hold iw_point data. """ 01207 01208 def __init__(self, data=None, flags=0): 01209 if data is None: 01210 raise ValueError, "data must be passed to Iwpoint" 01211 # P pointer to data, H length, H flags 01212 self.fmt = 'PHH' 01213 self.flags = flags 01214 self.buff = array.array('c', data) 01215 self.caddr_t, self.length = self.buff.buffer_info() 01216 self.packed_data = struct.pack(self.fmt, self.caddr_t, 01217 self.length, self.flags) 01218 01219 def update(self, packed_data): 01220 """ Updates the object attributes. """ 01221 self.packed_data = packed_data 01222 self.caddr_t, self.length, self.flags = struct.unpack(self.fmt, 01223 self.packed_data) 01224 01225 01226 class Iwrange(object): 01227 """ Holds iwrange struct. """ 01228 01229 def __init__(self, ifname): 01230 self.fmt = "IIIHB6Ii4B4BB" + flags.IW_MAX_BITRATES*"i" + \ 01231 "2i2i2i2i3H" + flags.IW_MAX_ENCODING_SIZES*"H" + \ 01232 "2BBHB" + flags.IW_MAX_TXPOWER*"i" + \ 01233 "2B3H2i2iHB" + flags.IW_MAX_FREQUENCIES*"ihBB" + \ 01234 "IiiHiI" 01235 01236 self.ifname = ifname 01237 self.errorflag = 0 01238 self.error = "" 01239 01240 # informative stuff 01241 self.throughput = 0 01242 01243 # nwid (or domain id) 01244 self.min_nwid = self.max_nwid = 0 01245 01246 # frequency for backward compatibility 01247 self.old_num_channels = self.old_num_frequency = self.old_freq = 0 01248 01249 # signal level threshold 01250 self.sensitivity = 0 01251 01252 # link quality 01253 self.max_qual = Iwquality() 01254 self.avg_qual = Iwquality() 01255 01256 # rates 01257 self.num_bitrates = 0 01258 self.bitrates = [] 01259 01260 # rts threshold 01261 self.min_rts = self.max_rts = 0 01262 01263 # fragmention threshold 01264 self.min_frag = self.max_frag = 0 01265 01266 # power managment 01267 self.min_pmp = self.max_pmp = 0 01268 self.min_pmt = self.max_pmt = 0 01269 self.pmp_flags = self.pmt_flags = self.pm_capa = 0 01270 01271 # encoder stuff 01272 self.encoding_size = 0 01273 self.num_encoding_sizes = self.max_encoding_tokens = 0 01274 self.encoding_login_index = 0 01275 01276 # transmit power 01277 self.txpower_capa = self.num_txpower = self.txpower = 0 01278 01279 # wireless extension version info 01280 self.we_vers_compiled = self.we_vers_src = 0 01281 01282 # retry limits and lifetime 01283 self.retry_capa = self.retry_flags = self.r_time_flags = 0 01284 self.min_retry = self.max_retry = 0 01285 self.min_r_time = self.max_r_time = 0 01286 01287 # frequency 01288 self.num_channels = self.num_frequency = 0 01289 self.frequencies = [] 01290 01291 # capabilities and power management 01292 self.enc_capa = 0 01293 self.min_pms = self.max_pms = self.pms_flags = 0 01294 self.modul_capa = 0 01295 self.bitrate_capa = 0 01296 01297 self.update() 01298 01299 def update(self): 01300 """ Updates Iwrange object by a system call to the kernel 01301 and updates internal attributes. 01302 01303 """ 01304 iwstruct = Iwstruct() 01305 buff, s = iwstruct.pack_wrq(640) 01306 status, result = iwstruct.iw_get_ext(self.ifname, 01307 flags.SIOCGIWRANGE, 01308 data=s) 01309 data = buff.tostring() 01310 self._parse(data) 01311 01312 def _parse(self, data): 01313 iwstruct = Iwstruct() 01314 result = iwstruct.parse_data(self.fmt, data) 01315 01316 # XXX there is maybe a much more elegant way to do this 01317 self.throughput, self.min_nwid, self.max_nwid = result[0:3] 01318 self.old_num_channels, self.old_num_frequency = result[3:5] 01319 self.old_freq = result[5:11] 01320 self.sensitivity = result[11] 01321 self.max_qual.setValues(result[12:16]) 01322 self.avg_qual.setValues(result[16:20]) 01323 self.num_bitrates = result[20] 01324 raw_bitrates = result[21:21+self.num_bitrates] 01325 for rate in raw_bitrates: 01326 if rate is not None: 01327 self.bitrates.append(rate) 01328 01329 self.min_rts, self.max_rts = result[53:55] 01330 self.min_frag, self.max_frag = result[55:57] 01331 self.min_pmp, self.max_pmp = result[57:59] 01332 self.min_pmt, self.max_pmt = result[59:61] 01333 self.pmp_flags, self.pmt_flags, self.pm_capa = result[61:64] 01334 self.encoding_size = result[64:72] 01335 self.num_encoding_sizes, self.max_encoding_tokens = result[72:74] 01336 self.encoding_login_index = result[74] 01337 self.txpower_capa, self.num_txpower = result[75:77] 01338 self.txpower = result[77:85] 01339 self.we_vers_compiled, self.we_vers_src = result[85:87] 01340 self.retry_capa, self.retry_flags, self.r_time_flags = result[87:90] 01341 self.min_retry, self.max_retry = result[90:92] 01342 self.min_r_time, self.max_r_time = result[92:94] 01343 self.num_channels = result[94] 01344 self.num_frequency = result[95] 01345 01346 freq = result[96:224] 01347 i = self.num_frequency 01348 for x in range(0, len(freq), 4): 01349 iwfreq = Iwfreq(freq[x:x+4]) 01350 fq = iwfreq.getFrequency() 01351 if fq is not None: 01352 self.frequencies.append(fq) 01353 i -= 1 01354 if i <= 0: 01355 break 01356 self.enc_capa = result[224] 01357 self.min_pms = result[225] 01358 self.max_pms = result[226] 01359 self.pms_flags = result[227] 01360 self.modul_capa = result[228] 01361 self.bitrate_capa = result[229] 01362 01363 01364 class Iwscan(object): 01365 """ Class to handle AP scanning. """ 01366 01367 def __init__(self, ifname, fullscan=True): 01368 """ Completes a scan for available access points, 01369 and returns them in Iwscanresult format. 01370 01371 fullscan: If False, data is read from a cache of the last scan 01372 If True, a scan is conducted, and then the data is read 01373 01374 """ 01375 self.ifname = ifname 01376 self.range = Iwrange(ifname) 01377 self.stream = None 01378 self.aplist = None 01379 self.index = -1 01380 01381 if fullscan: 01382 # Triggers the scan 01383 iwstruct = Iwstruct() 01384 datastr = iwstruct.pack("Pii", 0, 0, 0) 01385 status, result = iwstruct.iw_set_ext(self.ifname, 01386 flags.SIOCSIWSCAN, 01387 datastr) 01388 self.getScan() 01389 01390 def __iter__(self): 01391 return self 01392 01393 def __len__(self): 01394 return len(self.aplist) 01395 01396 def next(self): 01397 self.index = self.index + 1 01398 if self.index > len(self.aplist) - 1: 01399 raise StopIteration 01400 return self.aplist[self.index] 01401 01402 def getScan(self): 01403 """ Retrieves results, stored from the most recent scan. 01404 01405 """ 01406 iwstruct = Iwstruct() 01407 bufflen = flags.IW_SCAN_MAX_DATA 01408 01409 # Make repeated requests for scan with various recovery schemes 01410 while (True): 01411 buff, datastr = iwstruct.pack_wrq(bufflen) 01412 try: 01413 status, result = iwstruct.iw_get_ext(self.ifname, 01414 flags.SIOCGIWSCAN, 01415 data=datastr) 01416 except IOError, (error_number, error_string): 01417 if error_number == errno.E2BIG: 01418 # Keep resizing the buffer until it's 01419 # large enough to hold the scan 01420 pbuff, newlen = iwstruct.unpack('Pi', datastr) 01421 if bufflen < newlen: 01422 # the driver told us how big to make the buffer 01423 bufflen = newlen 01424 else: 01425 # try doubling the buffer size 01426 bufflen = bufflen * 2 01427 elif error_number == errno.EAGAIN: 01428 # Permission was NOT denied, 01429 # therefore we must WAIT to get results 01430 time.sleep(0.1) 01431 else: 01432 raise 01433 except: 01434 raise 01435 else: 01436 break 01437 01438 # unpack the buffer pointer and length 01439 pbuff, reslen = iwstruct.unpack('Pi', datastr) 01440 if reslen > 0: 01441 # Initialize the stream, and turn it into an enumerator 01442 self.aplist = self._parse(buff.tostring()) 01443 01444 def _parse(self, data): 01445 """ Parse the event stream, and return a list of Iwscanresult 01446 objects. 01447 01448 """ 01449 iwstruct = Iwstruct() 01450 scanresult = None 01451 aplist = [] 01452 01453 # Run through the stream until it is too short to contain a command 01454 while (len(data) >= flags.IW_EV_LCP_PK_LEN): 01455 # Unpack the header 01456 length, cmd = iwstruct.unpack('HH', 01457 data[:flags.IW_EV_LCP_PK_LEN]) 01458 # If the event length is too short to contain valid data, 01459 # then break, because we're probably at the end of the cell's data 01460 if length < flags.IW_EV_LCP_PK_LEN: 01461 break; 01462 # Put the events into their respective result data 01463 if cmd == flags.SIOCGIWAP: 01464 if scanresult: 01465 aplist.append(scanresult) 01466 scanresult = Iwscanresult( 01467 data[flags.IW_EV_LCP_PK_LEN:length], 01468 self.range) 01469 elif scanresult is None: 01470 raise RuntimeError("Attempting to add an event without AP data.") 01471 else: 01472 scanresult.addEvent(cmd, 01473 data[flags.IW_EV_LCP_PK_LEN:length]) 01474 # We're finished with the previous event 01475 data = data[length:] 01476 01477 # Don't forget the final result 01478 if scanresult: 01479 if scanresult.bssid != "00:00:00:00:00:00": 01480 aplist.append(scanresult) 01481 else: 01482 raise RuntimeError, 'Attempting to add an AP without a bssid' 01483 return aplist 01484 01485 01486 class Iwscanresult(object): 01487 """ An object to contain all the events associated with a single 01488 scanned AP. 01489 01490 """ 01491 01492 def __init__(self, data, iwrange): 01493 """ Initialize the scan result with the access point data. """ 01494 self.range = iwrange 01495 self.bssid = "%02X:%02X:%02X:%02X:%02X:%02X" % ( 01496 struct.unpack('BBBBBB', data[2:8])) 01497 self.essid = None 01498 self.mode = None 01499 self.rate = [] 01500 self.quality = Iwquality() 01501 self.frequency = None 01502 self.encode = None 01503 self.custom = [] 01504 self.protocol = None 01505 01506 def addEvent(self, cmd, data): 01507 """ Attempts to add the data from an event to a scanresult. 01508 Only certain data is accepted, in which case the result is True 01509 If the event data is invalid, None is returned 01510 If the data is valid but unused, False is returned 01511 01512 """ 01513 if ((cmd in range(flags.SIOCIWFIRST, 01514 flags.SIOCIWLAST+1)) or 01515 (cmd in range(flags.IWEVFIRST, 01516 flags.IWEVLAST+1))): 01517 if cmd == flags.SIOCGIWNWID: 01518 pass 01519 elif cmd == flags.SIOCGIWFREQ: 01520 self.frequency = Iwfreq(data) 01521 elif cmd == flags.SIOCGIWMODE: 01522 raw_mode = struct.unpack('I', data)[0] 01523 self.mode = flags.modes[raw_mode] 01524 elif cmd == flags.SIOCGIWNAME: 01525 self.protocol = data[:len(data)-2] 01526 elif cmd == flags.SIOCGIWESSID: 01527 self.essid = data[4:] 01528 elif cmd == flags.SIOCGIWENCODE: 01529 data = struct.unpack("B"*len(data), data) 01530 self.encode = Iwpoint("") 01531 self.encode.update(struct.pack('PHH', 01532 (int(data[0])<<16)+int(data[1]), data[2]<<8, data[3]<<8)) 01533 if (self.encode.caddr_t is None): 01534 self.encode.flags = \ 01535 self.encode.flags | flags.IW_ENCODE_NOKEY 01536 elif cmd == flags.SIOCGIWRATE: 01537 freqsize = struct.calcsize("ihbb") 01538 rates = [] 01539 while len(data) >= freqsize: 01540 m, e, dummy, pad = struct.unpack("ihbb", data[:freqsize]) 01541 if e == 0: 01542 rates.append(m) 01543 else: 01544 rates.append(m*10**e) 01545 data = data[freqsize:] 01546 self.rate.append(rates) 01547 elif cmd == flags.SIOCGIWMODUL: 01548 pass 01549 elif cmd == flags.IWEVQUAL: 01550 self.quality.parse(data) 01551 elif cmd == flags.IWEVGENIE: 01552 pass 01553 elif cmd == flags.IWEVCUSTOM: 01554 self.custom.append(data[1:]) 01555 else: 01556 raise ValueError("Unknown IW event command received. This " + \ 01557 "command cannot be used to add information " + \ 01558 "to the WiFi cell's profile.") 01559 else: 01560 raise ValueError("Invalid IW event command received. \ 01561 This command is not allowed.") 01562 01563 def display(self): 01564 print "ESSID:", self.essid 01565 print "Access point:", self.bssid 01566 print "Mode:", self.mode 01567 if len(self.rate) > 0: 01568 print "Highest Bitrate:", self.rate[len(self.rate)-1] 01569 print "Quality: Quality ", self.quality.quality, 01570 print "Signal ", self.quality.getSignallevel(), 01571 print " Noise ", self.quality.getNoiselevel() 01572 print "Encryption:", map(lambda x: hex(ord(x)), self.encode) 01573 # XXX 01574 # print "Frequency:", self.frequency.getFrequency(), "(Channel", self.frequency.getChannel(self.range), ")" 01575 for custom in self.custom: 01576 print "Custom:", custom 01577 print "" 01578