1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
|
# -*- coding: ISO-8859-1 -*-
# python-wifi -- a wireless library to access wireless cards via python
# Copyright (C) 2004, 2005, 2006 Róman Joost
#
# Contributions from:
# Mike Auty <m.auty@softhome.net> (Iwscanresult, Iwscan)
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
from struct import pack as struct_pack, \
unpack as struct_unpack, \
calcsize as struct_calcsize
from array import array
from math import ceil, log10
from fcntl import ioctl
from socket import AF_INET, SOCK_DGRAM, socket
from time import sleep
from re import compile
from flags import *
def getNICnames():
""" extract wireless device names of /proc/net/wireless
returns empty list if no devices are present
>>> getNICnames()
['eth1', 'wifi0']
"""
device = compile('[a-z]+[0-9]+')
ifnames = []
f = open('/proc/net/wireless', 'r')
data = f.readlines()
for line in data:
try:
ifnames.append(device.search(line).group())
except AttributeError:
pass
# if we couldn't lookup the devices, try to ask the kernel
if ifnames == []:
ifnames = getConfiguredNICnames()
return ifnames
def getConfiguredNICnames():
"""get the *configured* ifnames by a systemcall
>>> getConfiguredNICnames()
[]
"""
iwstruct = Iwstruct()
ifnames = []
buff = array('c', '\0'*1024)
caddr_t, length = buff.buffer_info()
s = iwstruct.pack('iP', length, caddr_t)
try:
result = iwstruct._fcntl(SIOCGIFCONF, s)
except IOError, (i, e):
return i, e
# get the interface names out of the buffer
for i in range(0, 1024, 32):
ifname = buff.tostring()[i:i+32]
ifname = struct_unpack('32s', ifname)[0]
ifname = ifname.split('\0', 1)[0]
if ifname:
# verify if ifnames are really wifi devices
wifi = Wireless(ifname)
result = wifi.getAPaddr()
if result[0] == 0:
ifnames.append(ifname)
return ifnames
def makedict(**kwargs):
return kwargs
class Wireless(object):
"""Access to wireless interfaces"""
def __init__(self, ifname):
self.sockfd = socket(AF_INET, SOCK_DGRAM)
self.ifname = ifname
self.iwstruct = Iwstruct()
def getAPaddr(self):
""" returns accesspoint mac address
>>> from iwlibs import Wireless, getNICnames
>>> ifnames = getNICnames()
>>> ifnames
['eth1', 'wifi0']
>>> wifi = Wireless(ifnames[0])
>>> wifi.getAPaddr()
'00:0D:88:8E:4E:93'
Test with non-wifi card:
>>> wifi = Wireless('eth0')
>>> wifi.getAPaddr()
(95, 'Operation not supported')
Test with non-existant card:
>>> wifi = Wireless('eth2')
>>> wifi.getAPaddr()
(19, 'No such device')
"""
buff, s = self.iwstruct.pack_wrq(32)
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWAP,
data=s)
if i > 0:
return result
return self.iwstruct.getMAC(result)
def getBitrate(self):
"""returns device currently set bit rate
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getBitrate()
'11 Mb/s'
"""
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWRATE)
if i > 0:
return result
iwfreq = Iwfreq(result)
return iwfreq.getBitrate()
def getBitrates(self):
"""returns the number of available bitrates for the device
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> num, rates = wifi.getBitrates()
>>> num == len(rates)
True
"""
range = Iwrange(self.ifname)
if range.errorflag:
return (range.errorflag, range.error)
return (range.num_bitrates, range.bitrates)
def getChannelInfo(self):
"""returns the number of channels and available frequency for
the device
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> num, rates = wifi.getChannelInfo()
>>> num == len(rates)
True
"""
range = Iwrange(self.ifname)
if range.errorflag:
return (range.errorflag, range.error)
return (range.num_channels, range.frequencies)
def getEssid(self):
"""get essid information
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getEssid()
'romanofski'
"""
essid = ""
buff, s = self.iwstruct.pack_wrq(32)
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWESSID,
data=s)
if i > 0:
return result
str = buff.tostring()
return str.strip('\x00')
def setEssid(self, essid):
"""set essid """
raise NotImplementedError
if len(essid) > IW_ESSID_MAX_SIZE:
return "essid to big"
buff, s = self.iwstruct.pack_test(essid, 32)
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCSIWESSID,
data=s)
if i > 0:
return result
def getEncryption(self):
"""get encryption information which is probably a string of '*',
'open', 'private'
as a normal user, you will get a 'Operation not permitted'
error:
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getEncryption()
(1, 'Operation not permitted')
"""
iwpoint = Iwpoint(self.ifname)
if iwpoint.errorflag:
return (iwpoint.errorflag, iwpoint.error)
return iwpoint.getEncryptionKey()
def getFragmentation(self):
"""returns fragmentation threshold
It depends on what the driver says. If you have fragmentation
threshold turned on, you'll get an int. If it's turned of
you'll get a string: 'off'.
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getFragmentation()
'off'
"""
iwparam = Iwparam(self.ifname, SIOCGIWFRAG)
if iwparam.errorflag:
return (iwparam.errorflag, iwparam.error)
return iwparam.getValue()
def getFrequency(self):
"""returns currently set frequency of the card
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getFrequency()
'2.417GHz'
"""
i, r = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWFREQ)
if i > 0:
return (i, r)
iwfreq = Iwfreq(r)
return iwfreq.getFrequency()
def getMode(self):
"""returns currently set operation mode
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getMode()
'Managed'
"""
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWMODE)
if i > 0:
return result
mode = self.iwstruct.unpack('i', result[:4])[0]
return modes[mode]
def setMode(self, mode):
"""sets the operation mode """
try:
this_modes = [x.lower() for x in modes]
mode = mode.lower()
wifimode = this_modes.index(mode)
except ValueError:
return "Invalid operation mode!"
s = self.iwstruct.pack('I', wifimode)
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCSIWMODE,
data=s)
if i > 0:
return result
def getWirelessName(self):
""" returns wireless name
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getWirelessName()
'IEEE 802.11-DS'
"""
i, result = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWNAME)
if i > 0:
return result
return result.split('\0')[0]
def getPowermanagement(self):
"""returns power management settings
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getPowermanagement()
'off'
"""
iwparam = Iwparam(self.ifname, SIOCGIWPOWER)
if iwparam.errorflag:
return (iwparam.errorflag, iwparam.error)
return iwparam.getValue()
def getRetrylimit(self):
"""returns limit retry/lifetime
man iwconfig:
Most cards have MAC retransmissions, and some allow to set
the behaviour of the retry mechanism.
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getRetrylimit()
16
"""
iwparam = Iwparam(self.ifname, SIOCGIWRETRY)
if iwparam.errorflag:
return (iwparam.errorflag, iwparam.error)
return iwparam.getValue()
def getRTS(self):
"""returns rts threshold
returns int, 'auto', 'fixed', 'off'
man iwconfig:
RTS/CTS adds a handshake before each packet transmission to
make sure that the channel is clear. This adds overhead, but
increases performance in case of hidden nodes or a large
number of active nodes. This parameter sets the size of the
smallest packet for which the node sends RTS; a value equal
to the maximum packet size disable the mechanism.
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getRTS()
'off'
"""
iwparam = Iwparam(self.ifname, SIOCGIWRTS)
if iwparam.errorflag:
return (iwparam.errorflag, iwparam.error)
return iwparam.getValue()
def getSensitivity(self):
"""returns sensitivity information
man iwconfig:
This is the lowest signal level for which the hardware
attempt packet reception, signals weaker than this are
ignored. This is used to avoid receiving background noise,
so you should set it according to the average noise
level. Positive values are assumed to be the raw value used
by the hardware or a percentage, negative values are
assumed to be dBm.
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getSensitivity()
'off'
"""
iwparam = Iwparam(self.ifname, SIOCGIWSENS)
if iwparam.errorflag:
return (iwparam.errorflag, iwparam.error)
return iwparam.getValue()
def getTXPower(self):
"""returns transmit power in dBm
>>> from iwlibs import Wireless
>>> wifi = Wireless('eth1')
>>> wifi.getTXPower()
'17 dBm'
"""
i, r = self.iwstruct.iw_get_ext(self.ifname,
SIOCGIWTXPOW)
if i > 0:
return (i, r)
iwfreq = Iwfreq(r)
return iwfreq.getTransmitPower()
def getStatistics(self):
"""returns statistics information which can also be found in
/proc/net/wireless
"""
iwstats = Iwstats(self.ifname)
if iwstats.errorflag > 0:
return (iwstats.errorflag, iwstats.error)
return [iwstats.status, iwstats.qual, iwstats.discard,
iwstats.missed_beacon]
def scan(self):
"""returns Iwscanresult objects, after a successful scan"""
iwscan = Iwscan(self.ifname)
return iwscan.scan()
class Iwstruct(object):
"""basic class to handle iwstruct data """
def __init__(self):
self.idx = 0
self.sockfd = socket(AF_INET, SOCK_DGRAM)
def parse_data(self, fmt, data):
""" unpacks raw C data """
size = struct_calcsize(fmt)
idx = self.idx
str = data[idx:idx + size]
self.idx = idx+size
value = struct_unpack(fmt, str)
# take care of a tuple like (int, )
if len(value) == 1:
return value[0]
else:
return value
def pack(self, fmt, *args):
""" calls struct_pack and returns the result """
return struct_pack(fmt, *args)
def pack_wrq(self, buffsize):
""" packs wireless request data for sending it to the kernel """
# Prepare a buffer
# We need the address of our buffer and the size for it. The
# ioctl itself looks for the pointer to the address in our
# memory and the size of it.
# Dont change the order how the structure is packed!!!
buff = array('c', '\0'*buffsize)
caddr_t, length = buff.buffer_info()
s = struct_pack('Pi', caddr_t, length)
return buff, s
def pack_test(self, string, buffsize):
""" packs wireless request data for sending it to the kernel """
buffsize = buffsize - len(string)
buff = array('c', string+'\0'*buffsize)
caddr_t, length = buff.buffer_info()
s = struct_pack('Pii', caddr_t, length, 1)
return buff, s
def unpack(self, fmt, packed_data):
""" unpacks data with given format """
return struct_unpack(fmt, packed_data)
def _fcntl(self, request, args):
return ioctl(self.sockfd.fileno(), request, args)
def iw_get_ext(self, ifname, request, data=None):
""" read information from ifname """
# put some additional data behind the interface name
if data is not None:
buff = IFNAMSIZE-len(ifname)
ifreq = ifname + '\0'*buff
ifreq = ifreq + data
else:
ifreq = (ifname + '\0'*32)
try:
result = self._fcntl(request, ifreq)
except IOError, (i, e):
return i, e
return (0, result[16:])
def getMAC(self, packed_data):
""" extracts mac addr from packed data and returns it as str """
mac_addr = struct_unpack('xxBBBBBB', packed_data[:8])
return "%02X:%02X:%02X:%02X:%02X:%02X" % mac_addr
class Iwparam(object):
"""class to hold iwparam data """
def __init__(self, ifname, ioctl):
# (i) value, (b) fixed, (b) disabled, (b) flags
self.fmt = "ibbH"
self.value = 0
self.fixed = 0
self.disabled = 0
self.flags = 0
self.errorflag = 0
self.error = ""
self.ioctl = ioctl
self.ifname = ifname
self.update()
def getValue(self):
"""returns the value if not disabled """
if self.disabled:
return 'off'
if self.flags & IW_RETRY_TYPE == 0:
return self.getRLAttributes()
else:
return self.getPMAttributes()
def getRLAttributes(self):
"""returns a string with attributes determined by self.flags
"""
return self.value
def getPMAttributes(self):
"""returns a string with attributes determined by self.flags
and IW_POWER*
"""
result = ""
# Modifiers
if self.flags & IW_POWER_MIN == 0:
result = " min"
if self.flags & IW_POWER_MAX == 0:
result = " max"
# Type
if self.flags & IW_POWER_TIMEOUT == 0:
result = " period:"
else:
result = " timeout:"
# Value with or without units
# IW_POWER_RELATIVE - value is *not* in s/ms/us
if self.flags & IW_POWER_RELATIVE:
result += "%f" %(float(self.value)/MEGA)
else:
if self.value >= MEGA:
result += "%fs" %(float(self.value)/MEGA)
elif self.value >= KILO:
result += "%fms" %(float(self.value)/KILO)
else:
result += "%dus" % self.value
return result
def update(self):
iwstruct = Iwstruct()
i, r = iwstruct.iw_get_ext(self.ifname,
self.ioctl)
if i > 0:
self.errorflag = i
self.error = r
self._parse(r)
def _parse(self, data):
""" unpacks iwparam data """
iwstruct = Iwstruct()
self.value, self.fixed, self.disabled, self.flags =\
iwstruct.parse_data(self.fmt, data)
class Iwfreq(object):
""" class to hold iwfreq data
delegates to Iwstruct class
"""
def __init__(self, data=None):
self.fmt = "ihbb"
if data is not None:
self.frequency = self.parse(data)
else:
self.frequency = 0
self.iwstruct = Iwstruct()
def __getattr__(self, attr):
return getattr(self.iwstruct, attr)
def parse(self, data):
""" unpacks iwparam"""
size = struct_calcsize(self.fmt)
m, e, i, pad = struct_unpack(self.fmt, data[:size])
# XXX well, its not *the* frequency - we need a better name
if e == 0:
return m
else:
return float(m)*10**e
def getFrequency(self):
"""returns Frequency (str)
data - binary data returned by systemcall (iw_get_ext())
"""
freq = self.frequency
if freq >= GIGA:
return "%0.3fGHz" %(freq/GIGA)
if freq >= MEGA:
return "%0.3fMHZ" %(freq/MEGA)
if freq >= KILO:
return "%0.3fKHz" %(freq/KILO)
def getBitrate(self):
""" returns Bitrate in Mbit
data - binary data returned by systemcall (iw_get_ext())
"""
bitrate = self.frequency
if bitrate >= GIGA:
return "%i Gb/s" %(bitrate/GIGA)
if bitrate >= MEGA:
return "%i Mb/s" %(bitrate/MEGA)
if bitrate >= KILO:
return "%i Kb/s" %(bitrate/KILO)
def getTransmitPower(self):
""" returns transmit power in dbm """
# XXX something flaky is going on with m and e
# eg. m = 50 and e should than be 0, because the number is stored in
# m and don't needs to be recalculated
return "%i dBm" %self.mw2dbm(self.frequency/10)
def getChannel(self, freq):
"""returns channel information given by frequency
returns None if frequency can't be converted
freq = frequency to convert (int)
iwrange = Iwrange object
"""
try:
freq = float(freq)
except:
return None
lut = {}
#13 Channels beginning at 2.412GHz and inreasing by 0,005 GHz steps
for i in range(0,12):
cur = float( 2.412 + ( i * 0.005 ) )
lut[str(cur)] = i+1
# Channel 14 need special actions ;)
lut['2.484'] = 14
if str(freq) in lut.keys():
return lut[str(freq)]
return None
def mw2dbm(self, mwatt):
""" converts mw to dbm(float) """
return ceil(10.0 * log10(mwatt))
def _setFrequency(self, list):
"""sets self.frequency by given list
currently only used by Iwrange
"""
assert len(list) == 4
m, e, i, pad = list
if e == 0:
self.frequency = m
else:
self.frequency = m #float(m)*10**e
class Iwstats(object):
""" class to hold iwstat data """
def __init__(self, ifname):
# (2B) status, 4B iw_quality, 6i iw_discarded
self.fmt = "2B4B6i"
self.status = 0
self.qual = Iwquality()
self.discard = {}
self.missed_beacon = 0
self.ifname = ifname
self.errorflag = 0
self.error = ""
self.update()
def update(self):
iwstruct = Iwstruct()
buff, s = iwstruct.pack_wrq(32)
i, result = iwstruct.iw_get_ext(self.ifname,
SIOCGIWSTATS,
data=s)
if i > 0:
self.error = result
self.errorflag = i
self._parse(buff.tostring())
def _parse(self, data):
""" unpacks iwstruct data """
struct = Iwstruct()
iwqual = Iwquality()
iwstats_data = struct.parse_data(self.fmt, data)
self.status = iwstats_data[0:2]
self.qual.quality, self.qual.sl, self.qual.nl,\
self.qual.flags = iwstats_data[2:6]
nwid, code, frag, retries, flags = iwstats_data[6:11]
self.missed_beacon = iwstats_data[11:12][0]
self.discard = makedict(nwid=nwid, code=code,
fragment=frag, retries=retries, misc=flags)
class Iwquality(object):
""" class to hold iwquality data """
def __init__(self):
self.quality = 0
self.sl = 0
self.nl = 0
self.updated = 0
self.fmt = "4B"
def parse(self, data):
""" unpacks iwquality data """
struct = Iwstruct()
qual, sl, nl, flags = struct.parse_data(self.fmt, data)
# compute signal and noise level
self.signal_level = sl
self.noise_level = nl
# asign the other values
self.quality = qual
self.updated = flags
def setValues(self, list):
""" assigns values given by a list to our attributes """
attributes = ["quality", "signallevel", "noise_level",
"updated"]
assert len(list) == 4
for i in range(len(list)):
setattr(self, attributes[i], list[i])
def getSignallevel(self):
""" returns signal level """
return self.sl-0x100
def setSignallevel(self, sl):
""" sets signal level """
self.sl = sl
signallevel = property(getSignallevel, setSignallevel)
def getNoiselevel(self):
""" returns noise level """
return self.nl - 0x100
def setNoiselevel(self):
raise NotImplementedError
self.nl = nl
noiselevel = property(getNoiselevel, setNoiselevel)
class Iwpoint(object):
""" class to hold iwpoint data """
def __init__(self, ifname):
self.key = [0,0,0,0]
self.fields = 0
self.flags = 0
# (4B) pointer to data, H length, H flags
self.fmt = "4BHH"
self.errorflag = 0
self.error = ""
self.ifname = ifname
self.update()
def __getattr__(self, attr):
return getattr(self.iwstruct, attr)
def update(self):
iwstruct = Iwstruct()
buff, s = iwstruct.pack_wrq(32)
i, result = iwstruct.iw_get_ext(self.ifname,
SIOCGIWENCODE,
data=s)
if i > 0:
self.errorflag = i
self.error = result
self._parse(result)
def getEncryptionKey(self):
""" returns encryption key as '**' or 'off' as str """
if self.flags & IW_ENCODE_DISABLED != 0:
return 'off'
elif self.flags & IW_ENCODE_NOKEY != 0:
# a key is set, so print it
return '**' * self.fields
def _parse(self, data):
""" unpacks iwpoint data
"""
iwstruct = Iwstruct()
ptr, ptr, ptr, ptr, self.fields, self.flags =\
iwstruct.parse_data(self.fmt, data)
self.key = [ptr, ptr, ptr, ptr]
class Iwrange(object):
"""holds iwrange struct """
IW_MAX_FREQUENCIES = 32
def __init__(self, ifname):
self.fmt = "iiihb6ii4B4Bi32i2i2i2i2i3h8h2b2bhi8i2b3h2i2ihB17x"\
+ self.IW_MAX_FREQUENCIES*"ihbb"
self.ifname = ifname
self.errorflag = 0
self.error = ""
# informative stuff
self.throughput = 0
# nwid (or domain id)
self.min_nwid = self.max_nwid = 0
# frequency for backward compatibility
self.old_num_channels = self.old_num_frequency = self.old_freq = 0
# signal level threshold
self.sensitivity = 0
# link quality
self.max_qual = Iwquality()
self.avg_qual = Iwquality()
# rates
self.num_bitrates = 0
self.bitrates = []
# rts threshold
self.min_rts = self.max_rts = 0
# fragmention threshold
self.min_frag = self.max_frag = 0
# power managment
self.min_pmp = self.max_pmp = 0
self.min_pmt = self.max_pmt = 0
self.pmp_flags = self.pmt_flags = self.pm_capa = 0
# encoder stuff
self.encoding_size = 0
self.num_encoding_sizes = self.max_encoding_tokens = 0
self.encoding_login_index = 0
# transmit power
self.txpower_capa = self.num_txpower = self.txpower = 0
# wireless extension version info
self.we_vers_compiled = self.we_vers_src = 0
# retry limits and lifetime
self.retry_capa = self.retry_flags = self.r_time_flags = 0
self.min_retry = self.max_retry = 0
self.min_r_time = self.max_r_time = 0
# frequency
self.num_channels = self.num_frequency = 0
self.frequencies = []
self.update()
def update(self):
"""updates Iwrange object by a system call to the kernel
and updates internal attributes
"""
iwstruct = Iwstruct()
buff, s = iwstruct.pack_wrq(640)
i, result = iwstruct.iw_get_ext(self.ifname,
SIOCGIWRANGE,
data=s)
if i > 0:
self.errorflag = i
self.error = result
data = buff.tostring()
self._parse(data)
def _parse(self, data):
struct = Iwstruct()
result = struct.parse_data(self.fmt, data)
# XXX there is maybe a much more elegant way to do this
self.throughput, self.min_nwid, self.max_nwid = result[0:3]
self.old_num_channels, self.old_num_frequency = result[3:5]
self.old_freq = result[5:11]
self.sensitivity = result[11]
self.max_qual.setValues(result[12:16])
self.avg_qual.setValues(result[16:20])
self.num_bitrates = result[20] # <- XXX
raw_bitrates = result[21:53]
for rate in raw_bitrates:
iwfreq = Iwfreq()
iwfreq.frequency = rate
br = iwfreq.getBitrate()
if br is not None:
self.bitrates.append(br)
self.min_rts, self.max_rts = result[53:55]
self.min_frag, self.max_frag = result[55:57]
self.min_pmp, self.max_pmp = result[57:59]
self.min_pmt, self.max_pmt = result[59:61]
self.pmp_flags, self.pmt_flags, self.pm_capa = result[61:64]
self.encoding_size = result[64:72]
self.num_encoding_sizes, self.max_encoding_tokens = result[72:74]
self.encoding_login_index = result[74:76]
self.txpower_capa, self.num_txpower = result[76:78]
self.txpower = result[78:86]
self.we_vers_compiled, self.we_vers_src = result[86:88]
self.retry_capa, self.retry_flags, self.r_time_flags = result[88:91]
self.min_retry, self.max_retry = result[91:93]
self.min_r_time, self.max_r_time = result[93:95]
self.num_channels = result[95]
self.num_frequency = result[96]
freq = result[97:]
i = self.num_frequency
for x in range(0, len(freq), 4):
iwfreq = Iwfreq()
iwfreq._setFrequency(freq[x:x+4])
fq = iwfreq.getFrequency()
if fq is not None:
self.frequencies.append(fq)
i -= 1
if i <= 0:
break
class Iwscan(object):
"""class to handle AP scanning"""
def __init__(self, ifname):
self.ifname = ifname
self.range = Iwrange(ifname)
self.errorflag = 0
self.error = ""
self.stream = None
self.aplist = None
def scan(self, fullscan=True):
"""Completes a scan for available access points,
and returns them in Iwscanresult format
fullscan: If False, data is read from a cache of the last scan
If True, a scan is conducted, and then the data is read
"""
# By default everything is fine, do not wait
result = 1
if fullscan:
self.setScan()
if self.errorflag > EPERM:
raise RuntimeError, 'setScan failure ' + str(self.errorflag) + " " + str(self.error)
return None
elif self.errorflag < EPERM:
# Permission was NOT denied, therefore we must WAIT to get results
result = 250
while (result > 0):
sleep(result/1000)
result = self.getScan()
if result < 0 or self.errorflag != 0:
raise RuntimeError, 'getScan failure ' + str(self.errorflag) + " " + str(self.error)
return self.aplist
def setScan(self):
"""Triggers the scan, if we have permission
"""
iwstruct = Iwstruct()
s = iwstruct.pack('Pii', 0, 0, 0)
i, result = iwstruct.iw_get_ext(self.ifname,
SIOCSIWSCAN,s)
if i > 0:
self.errorflag = i
self.error = result
return result
def getScan(self):
"""Retreives results, stored from the most recent scan
Returns 0 if successful, a delay if the data isn't ready yet
or -1 if something really nasty happened
"""
iwstruct = Iwstruct()
i = E2BIG
bufflen = IW_SCAN_MAX_DATA
# Keep resizing the buffer until it's large enough to hold the scan
while (i == E2BIG):
buff, s = iwstruct.pack_wrq(bufflen)
i, result = iwstruct.iw_get_ext(self.ifname,
SIOCGIWSCAN,
data=s)
if i == E2BIG:
pbuff, newlen = iwstruct.unpack('Pi', s)
if bufflen < newlen:
bufflen = newlen
else:
bufflen = bufflen * 2
if i == EAGAIN:
return 100
if i > 0:
self.errorflag = i
self.error = result
return -1
pbuff, reslen = iwstruct.unpack('Pi', s)
if reslen > 0:
# Initialize the stream, and turn it into an enumerator
self.aplist = self._parse(buff.tostring())
return 0
def _parse(self, data):
"""Parse the event stream, and return a list of Iwscanresult objects
"""
iwstruct = Iwstruct()
scanresult = None
aplist = []
# Run through the stream, until broken
while 1:
# If we're the stream doesn't have enough space left for a header, break
if len(data) < IW_EV_LCP_LEN:
break;
# Unpack the header
length, cmd = iwstruct.unpack('HH', data[:4])
# If the header says the following data is shorter than the header, then break
if length < IW_EV_LCP_LEN:
break;
# Put the events into their respective result data
if cmd == SIOCGIWAP:
if scanresult is not None:
aplist.append(scanresult)
scanresult = Iwscanresult(data[IW_EV_LCP_LEN:length], self.range)
elif scanresult is None:
raise RuntimeError, 'Attempting to add an event without AP data'
else:
scanresult.addEvent(cmd, data[IW_EV_LCP_LEN:length])
# We're finished with the preveious event
data = data[length:]
# Don't forgset the final result
if scanresult.bssid != "00:00:00:00:00:00":
aplist.append(scanresult)
else:
raise RuntimeError, 'Attempting to add an AP without a bssid'
return aplist
class Iwscanresult(object):
"""An object to contain all the events associated with a single scanned AP
"""
def __init__(self, data, range):
"""Initialize the scan result with the access point data"""
self.iwstruct = Iwstruct()
self.range = range
self.bssid = "%02X:%02X:%02X:%02X:%02X:%02X" % struct_unpack('BBBBBB', data[2:8])
self.essid = None
self.mode = None
self.rate = []
self.quality = Iwquality()
self.frequency = None
self.encode = None
self.custom = []
self.protocol = None
def addEvent(self, cmd, data):
"""Attempts to add the data from an event to a scanresult
Only certain data is accept, in which case the result is True
If the event data is invalid, None is returned
If the data is valid but unused, False is returned
"""
if cmd <= SIOCIWLAST:
if cmd < SIOCIWFIRST:
return None
elif cmd >= IWEVFIRST:
if cmd > IWEVLAST:
return None
else:
return None
if cmd == SIOCGIWESSID:
self.essid = data[4:]
elif cmd == SIOCGIWMODE:
self.mode = modes[self.iwstruct.unpack('i', data[:4])[0]]
elif cmd == SIOCGIWRATE:
# TODO, deal with multiple rates, or at least the highest rate
freqsize = struct_calcsize("ihbb")
while len(data) >= freqsize:
iwfreq = Iwfreq(data)
self.rate.append(iwfreq.getBitrate())
data = data[freqsize:]
elif cmd == IWEVQUAL:
self.quality.parse(data)
elif cmd == SIOCGIWFREQ:
self.frequency = Iwfreq(data)
elif cmd == SIOCGIWENCODE:
self.encode = data
elif cmd == IWEVCUSTOM:
self.custom.append(data[1:])
elif cmd == SIOCGIWNAME:
self.protocol = data[:len(data)-2]
else:
#print "Cmd:", cmd
return False
return True
|