From cee9f7f9aa6b149ec36794707c02fa6bda36ba21 Mon Sep 17 00:00:00 2001 From: wraith-wireless Date: Thu, 16 Jun 2016 17:58:52 -0600 Subject: [PATCH] v0.1.3 Added five new functions, libnl fixes --- CHANGES | 92 ++++++++++++ MANIFEST.in | 2 +- README.md | 5 +- TODO | 2 + docs/PyRIC.pdf | Bin 491058 -> 492116 bytes docs/PyRIC.tex | 21 ++- pyric/__init__.py | 122 +--------------- pyric/lib/__init__.py | 1 + pyric/lib/libnl.py | 9 +- pyric/pyw.py | 318 ++++++++++++++++++++++++++++++++---------- setup.py | 2 +- 11 files changed, 371 insertions(+), 203 deletions(-) create mode 100644 CHANGES diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..dd32496 --- /dev/null +++ b/CHANGES @@ -0,0 +1,92 @@ +PyRIC CHANGES + +v 0.0.2 to 0.1.0 + o added ifconfig/iwconfig functions to pyw + o reworked exception handling + - all exceptions from libnl, libio & pyw are pyric.error + - pyw will allow pyric to pass through + - reworked errorcodes to derive from errno + o added _iostub_, _nlstub_ and reworked traditiona commands to utilize these + o finished porting nl80211_h and nl80211_c (for attribute policies) + o pyw no longer provides familyid as a public function, rather it now uses a + private global value for the nl80211 family id and will instantiate it one + time only. In this way, callers do not not have to worry about retrieving and + passing it + o regdom get & set implemented + o info implemented + o removed radio/Radio class (shouldn't be the responsibility of this) + o added channels.py (provides channel/freq functions) + o added RFI page for notes/observations/questions + o changed utils.py to device.py + o updated libnl + o added channel set & get + - channel get only works when device is associated + - channel set only works when card is in monitor mode and all other interfaces + have been deleted + o added device add & delete + o rewrote pyw function to handle one-time & persistent functions using a + single function interface for each command + o added Card class and wrote functions to handle it in pyw + o implemented basic help functionality (for nl80211) + o added monitor flag(s) support in devadd + o began work on a user guide + o added nested attribute handling + o added partial phyinfo handles all but supported channels/bands + o fixed bugs in devinfo and phyinfo + o added setup.py and required files + o at least one card (ath9k_htc) has an unknown supported command, added a + wrapper around the list IFTYPES to handle commands not listed + o libnl: attribute related i.e. nla_* moved out of GENLMsg class and made as + standalone functions + o in pyw + - added modeset/modeget in pyw + - readded freqset in pyw + - added devcmds in pyw + - annotated (in comments) if fcts needed root privileges + - added functions to get/set ip address, netmask and broadcast + - fixed PEP8 errors + - added function pulling supported freqs out NL80211_ATTR_WIPHY_BANDS + * ATT using _getfreqs_ which attempts to find the packed version of every + freq + * nl80211_c.nl80211_parse_freqs works but is slower than _getfreqs_ and + uses several hacks which may make it invalid for certain cards + - added 4.9GHz frequencies to channels.py + - fixed _validmac_ to validate both uppercase & lowercase hex characters + - added devfreqs, devchs + o unittest completed 61 tests ran in 5.360s + o added rfkill (still working on it) + o production release + +v 0.1.1 - 0.1.2 + desc: wireless nic library: wireless radio identification, manipulation, enumeration + includes: /nlhelp /lib /net /utils pyw 0.1.2 + changes: + o restructured hierarchy + - renamed device to hardware + o added mac address related functions to hardware + o split rfkill into functions file and definition file + o added block/unblock to pyw + o updated pyw to handle the newest version of libnl.py's processing of nested + attributes + +v 0.1.3 + o added this File, a revision history + o fixed issues + - # 13: Failed to process commands.help + - # 14: nlmsg_fromstream can incorrectly return error based on stream size + - # 11: MemoryError on nla_parse_nested() + o added nla_put_flag to libnl + o identified (unopened issue) and fixed error with ouifetch having invalid path + of default oui file + o moved parseoui from hardware to ouifetch renaming to parse + o removed ouifetch execution ability - parse and fetch must be called w/in + python console. Will eventually add this capability as as top-level program + o added ieee80211_h.py to net/wireless + o added supported ciphers to pyw.phyinfo dict + o added functions + - pwrsaveset and pwrsaveget + - covclassset + - retryshortset + - retrylongset + - rtsthreshset + - fragthreshset \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 89eec76..956b44e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ # setup paramaters -include LICENSE README.md TODO +include LICENSE CHANGES README.md TODO # Include subdirectories # note to self: even including recursive-include here, have to follow up by diff --git a/README.md b/README.md index 718ed2f..90f666c 100644 --- a/README.md +++ b/README.md @@ -357,10 +357,13 @@ w0 ``` #### iv. Additional Functions +PyRIC also provides functions to change Radio Parameters: coverage class, rts and +frag thresholds, and retry limits short and long + Read the user guide, or type dir(pyw) in your console to get a full listing of pyw functions. -c. Miscelleaneous Utilities +**** v. Miscelleaneous Utilities Several additional tools are located in the utils directory. Two of these are: * channels.py: defines ISM and UNII band channels/frequencies and provides functions to convert between channel and frequency and vice-versa diff --git a/TODO b/TODO index c5e4b1b..cc24f19 100644 --- a/TODO +++ b/TODO @@ -25,6 +25,8 @@ 11) tried NL80211_CMD_GET_WIPHY, setting _WIPHY_TX_POWER_LEVEL but did not return the current power level - currently cannot find anything in nl80211.h that could be used to get tx power + 12) add set retry short and long, set rts and set frag + 13) replace hardcoded values like cc limits in setcovclass to constants ------- Request For Information RFI ---------- diff --git a/docs/PyRIC.pdf b/docs/PyRIC.pdf index 0fbf0ec7cd8964f1a0ab9ce70a71c341158eba8b..8f6dda10c9e7ca087c6d5e9f75e191d0a4185107 100644 GIT binary patch delta 35537 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 delta 34519 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 diff --git a/docs/PyRIC.tex b/docs/PyRIC.tex index e5c5ed3..f132432 100644 --- a/docs/PyRIC.tex +++ b/docs/PyRIC.tex @@ -19,7 +19,7 @@ % % __name__ = 'User Guide' %__license__ = 'GPLv3' -%__version__ = '0.0.3' +%__version__ = '0.0.4' %__date__ = 'June 2016' %__author__ = 'Dale Patterson' %__maintainer__ = 'Dale Patterson' @@ -73,7 +73,7 @@ basicstyle=\footnotesize } -\title{\includegraphics[scale=1]{logo}\\ PyRIC v0.1.2: User Manual} +\title{\includegraphics[scale=1]{logo}\\ PyRIC v0.1.3: User Manual} \author{Dale V. Patterson\\ wraith.wireless@yandex.com} \begin{document} @@ -687,6 +687,7 @@ \section{API: pyw.py}\label{sec:pywapi} \subsection{Constants} \begin{itemize} \item \textbf{\_FAM80211ID\_}: Global netlink family id of nl80211. Do not touch +\item \textbf{\_MAXTHRESH\_}: defines limit for thresholds or 'off'. Do not touch \item \textbf{IFTYPES}: redefined (from nl80211\_h.py) interface modes \item \textbf{MNTRFLAGS}: redefined (from nl80211\_h.py) monitor mode flags \item \textbf{IPADDR}: Regular Expression for ip4 address validation @@ -745,10 +746,20 @@ \subsection{Functions} \item block(card): (rfkill block ) type: N/A, soft blocks card \item unblock(card): (rfkill unblock ) type: N/A, removes the soft block on card -\item getpwrsave(card,[nlsock]) (iw dev card. get power\_save) type: netlink +\item pwrsaveget(card,[nlsock]) (iw dev card. get power\_save) type: netlink get card's power save state True = on, False = off -\item setpwrsave(card,on,[nlsock]) (iw dev card. set power\_save ) type: +\item pwrsaveset(card,on,[nlsock]) (iw dev card. set power\_save ) type: netlink set card's power save state True = on, False = off +\item covclassset(card,cc,[nlsock]) (iw phy card. set coverage ) type: +netlink set card's coverage class +\item retryshortset(card,lim,[nlsock]) (iw phy card. set retry short ) +type:netlink set card's retry short limit +\item retrylongset(card,lim,[nlsock]) (iw phy card. set retry long ) +type:netlink set card's retry long limit +\item rtsthreshset(card,thresh,[nlsock]) (iw phy card. set rts ) type: +netlink set card's RTS threshold +\item fragthreshset(card,thresh,[nlsock]) (iw phy card. set frag ) type: +netlink set card's fragmentation threshold \item inetget(card,[iosock]): (ifconfig card.), type: ioctl, get ip4 address, netmask and broadcast address of card \item inetset(card,ipaddr,netmask,broadcast,[iosock]): (ifconfig card/ @@ -1069,7 +1080,7 @@ \subsection{Functions} \end{enumerate} \section{Copyright and License}\label{sec:copy} -PYRIC: Python Radio Interface Controller v0.1.0\\ +PYRIC: Python Radio Interface Controller v0.1.3\\ Copyright (C) 2016 Dale V. Patterson (wraith.wireless@yandex.com)\\ diff --git a/pyric/__init__.py b/pyric/__init__.py index f3d5f0a..513e874 100644 --- a/pyric/__init__.py +++ b/pyric/__init__.py @@ -25,132 +25,18 @@ Requires: linux (preferred 3.x kernel) Python 2.7 - - pyric 0.0.2 - desc: wireless nic (radio) manipulation, enumeration, and attribute enumeration - includes: /net /lib pyw 0.0.3 radio 0.0.4 utils 0.0.2 - changes: - o added ifconfig/iwconfig functions to pyw - o reworked exception handling - - all exceptions from libnl, libio & pyw are pyric.error - - pyw will allow pyric to pass through - - reworked errorcodes to derive from errno - o added _iostub_, _nlstub_ and reworked traditiona commands to utilize these - o finished porting nl80211_h and nl80211_c (for attribute policies) - o pyw no longer provides familyid as a public function, rather it now uses a - private global value for the nl80211 family id and will instantiate it one - time only. In this way, callers do not not have to worry about retrieving and - passing it - o regdom get & set implemented - o info implemented - pyric 0.0.3 - desc: wireless nic (radio) manipulation, enumeration, and attribute enumeration - includes: /net /lib pyw 0.0.3 device 0.0.3 channels 0.0.1 - changes: - o removed radio/Radio class (shouldn't be the responsibility of this) - o added channels.py (provides channel/freq functions) - o added RFI page for notes/observations/questions - o changed utils.py to device.py - o updated libnl - o added channel set & get - - channel get only works when device is associated - - channel set only works when card is in monitor mode and all other interfaces - have been deleted - o added device add & delete - - pyric 0.0.4 - desc: wireless nic (radio) manipulation, enumeration, and attribute enumeration - includes: /net /lib pyw 0.1.0 device 0.0.3 channels 0.0.1 setup 0.0.2 - changes: - o rewrote pyw function to handle one-time & persistent functions using a - single function interface for each command - - pyric 0.0.5 - desc: wireless nic (radio) manipulation, enumeration, and attribute enumeration - includes: /net /lib /docs pyw 0.1.2 device 0.0.3 channels 0.0.1 - changes: - o added Card class and wrote functions to handle it in pyw - o implemented basic help functionality (for nl80211) - o added monitor flag(s) support in devadd - o began work on a user guide - o added nested attribute handling - o added partial phyinfo handles all but supported channels/bands - o fixed bugs in devinfo and phyinfo - o added setup.py and required files - - pyric 0.0.6 - desc: Pythonic iw - wireless nic (radio) manipulation, enumeration, and attribute - enumeration - includes: /docs /examples /lib /net pyw 0.1.2 device 0.0.3 channels 0.0.1 - changes: - o move pyric under pyric to facilitate setuptools and packaging - - moved LICENSE, MANIFEST.in README.md setup.cfg setup.py examples/ PyRIC.pdf - to outer pyric - o at least one card (ath9k_htc) has an unknown supported command, added a - wrapper around the list IFTYPES to handle commands not listed - - pyric 0.0.7 - desc: Pythonic iw - wireless nic (radio) manipulation, enumeration, and attribute - enumeration - includes: /docs /examples /lib /net pyw 0.1.2 device 0.0.3 channels 0.0.1 - changes: - o libnl: attribute related i.e. nla_* moved out of GENLMsg class and made as - standalone functions - o in pyw - - added modeset/modeget in pyw - - readded freqset in pyw - - added devcmds in pyw - - annotated (in comments) if fcts needed root privileges - - added functions to get/set ip address, netmask and broadcast - - fixed PEP8 errors - - added function pulling supported freqs out NL80211_ATTR_WIPHY_BANDS - * ATT using _getfreqs_ which attempts to find the packed version of every - freq - * nl80211_c.nl80211_parse_freqs works but is slower than _getfreqs_ and - uses several hacks which may make it invalid for certain cards - - added 4.9GHz frequencies to channels.py - - fixed _validmac_ to validate both uppercase & lowercase hex characters - - added devfreqs, devchs - o unittest completed 61 tests ran in 5.360s - o added rfkill (still working on it) - - pyric 0.1.0 - desc: Pythonic iw - wireless nic (radio) manipulation, enumeration, and attribute - enumeration - includes: /docs /examples /lib /net pyw 0.1.2 device 0.0.3 channels 0.0.1 - changes: - o production release - - pyric 0.1.1 t0 0.1.1-* - desc: wireless nic library: wireless radio identification, manipulation, enumeration - includes: /nlhelp /lib /net /utils pyw 0.1.2 - changes: - o restructured hierarchy - - renamed device to hardware - o added mac address related functions to hardware - o split rfkill into functions file and definition file - o added block/unblock to pyw - o updated pyw to handle the newest version of libnl.py's processing of nested - attributes - - pyric 0.1.2 + pyric 0.1.3 desc: wireless nic library: wireless radio identification, manipulation, enumeration includes: /nlhelp /lib /net /utils pyw 0.1.4 changes: - o fixed install issues via pip where data files were not being loaded onto - the user's system - o identified and fixed error with ouifetch with invalid path of default oui file - o moved parseoui from hardware to ouifetch renaming to parse - o removed ouifetch execution ability - parse and fetch must be called w/in - python console. Will eventually add this capability as as top-level program - o added supported ciphers to pyw.phyinfo dict - o added functions getpwrsave and setpwrsave pyw + See CHANGES in top-level directory + """ __name__ = 'pyric' __license__ = 'GPLv3' -__version__ = '0.1.2.1' +__version__ = '0.1.3' __date__ = 'June 2016' __author__ = 'Dale Patterson' __maintainer__ = 'Dale Patterson' diff --git a/pyric/lib/__init__.py b/pyric/lib/__init__.py index 360cb67..ff77081 100644 --- a/pyric/lib/__init__.py +++ b/pyric/lib/__init__.py @@ -37,6 +37,7 @@ and not full blow objects - added some error checking to nla_parse_nested, raising and handling error in situations where attribute len is 0 + - fixed nlmsg_fromstream to handle non-ack message parsing with a length of 36 """ __name__ = 'lib' diff --git a/pyric/lib/libnl.py b/pyric/lib/libnl.py index 25e395d..5259c99 100644 --- a/pyric/lib/libnl.py +++ b/pyric/lib/libnl.py @@ -424,16 +424,17 @@ def nlmsg_new(nltype=None,cmd=None,seq=None,pid=None,flags=None,attrs=None): 'cmd':cmd or genlh.CTRL_CMD_UNSPEC, 'attrs':attrs or []}) -def nlmsg_fromstream(stream): +def nlmsg_fromstream(stream,override=False): """ create a GENLMsg from a stream :param stream: packed binary data + :param override: override ack processings - DO NOT USE for debugging only :returns: a GENLMsg """ # parse out netlink/generic netlink headers try: l,t,fs,s,p = struct.unpack_from(nlh.nl_nlmsghdr,stream,0) - if t == nlh.NLMSG_ERROR or l == nlh.NLMSGACKLEN: + if t == nlh.NLMSG_ERROR or (l == nlh.NLMSGACKLEN and not override): # have an (possible) ack/nack i.e. error msg e = struct.unpack_from(nlh.nl_nlmsgerr,stream,nlh.NLMSGHDRLEN)[0] raise pyric.error(abs(e),strerror(abs(e))) @@ -554,7 +555,7 @@ def nla_put(msg,v,a,d): msg['attrs'].append((a,v,d)) # nla_put_* append data of specified datatype -def nla_put_flag(msg,a): nla_put(msg,None,a,nlh.NLA_FLAG) +def nla_put_flag(msg,a): nla_put(msg,) def nla_put_unspec(msg,v,a): nla_put(msg,v,a,nlh.NLA_UNSPEC) def nla_put_u8(msg,v,a): nla_put(msg,v,a,nlh.NLA_U8) def nla_put_u16(msg,v,a): nla_put(msg,v,a,nlh.NLA_U16) @@ -667,4 +668,4 @@ def _maxbufsz_(): # return a hardcoded value return 2097152 finally: - if fin: fin.close() \ No newline at end of file + if fin: fin.close() diff --git a/pyric/pyw.py b/pyric/pyw.py index 6362204..e1dd015 100644 --- a/pyric/pyw.py +++ b/pyric/pyw.py @@ -92,6 +92,7 @@ import pyric.lib.libio as io # ioctl functions _FAM80211ID_ = None +_MAXTHRESH_ = 4294967295 #(2^32 -1 or the max value of a u32) # redefine interface types and monitor flags IFTYPES = nl80211h.NL80211_IFTYPES @@ -143,8 +144,6 @@ def winterfaces(*argv): wifaces = [] for dev in interfaces(): try: - #_ = io.io_transfer(iosock,sioch.SIOCGIWNAME,ifh.ifreq(dev)) - #wifaces.append(dev) if iswireless(dev, iosock): wifaces.append(dev) except pyric.error as e: # ENODEV & EOPNOTSUPP mean not wireless, reraise any others @@ -168,8 +167,6 @@ def iswireless(dev, *argv): # if the call succeeds, found to be wireless _ = io.io_transfer(iosock, sioch.SIOCGIWNAME, ifh.ifreq(dev)) return True - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) except pyric.error as e: # ENODEV or ENOTSUPP means not wireless, reraise any others if e.errno == errno.ENODEV or e.errno == errno.EOPNOTSUPP: return False @@ -199,7 +196,7 @@ def regset(rd, *argv): sets the current regulatory domain (iw reg set ) :param rd: regulatory domain code :param argv: netlink socket at argv[0] (or empty) - :returns: the two charactor regulatory domain + :returns: True on success """ if len(rd) != 2: raise pyric.error(errno.EINVAL, "Invalid reg. domain") try: @@ -216,10 +213,8 @@ def regset(rd, *argv): return True # we got here-it worked (or there were no complaints) ################################################################################ -#### WIRELESS INTERFACE FUNCTIONS #### -################################################################################ - #### CARD RELATED #### +################################################################################ class Card(tuple): """ @@ -270,7 +265,9 @@ def validcard(card, *argv): if e.errno == errno.ENODEV: return False else: raise +############################################################################### #### ADDRESS RELATED #### +################################################################################ def macget(card, *argv): """ @@ -292,10 +289,10 @@ def macget(card, *argv): return _hex2mac_(ret[18:24]) else: raise pyric.error(errno.EAFNOSUPPORT, "Invalid return addr family {0}".format(fam)) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "Error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error: {0}".format(e)) def macset(card, mac, *argv): """ @@ -320,10 +317,10 @@ def macset(card, mac, *argv): return _hex2mac_(ret[18:24]) else: raise pyric.error(errno.EAFNOSUPPORT, "Returned hw address family is not valid") - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def inetget(card, *argv): """ @@ -369,10 +366,10 @@ def inetget(card, *argv): # catch error where no addresses are assigned to card if e.errno == errno.EADDRNOTAVAIL: return ip4, netmask, brdaddr else: raise - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) return ip4, netmask, brdaddr @@ -421,10 +418,10 @@ def inetset(card, ipaddr, netmask, broadcast, *argv): raise pyric.error(errno.EINVAL, "Cannot set netmask/broadcast. Set ip first") else: raise - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "ifreq error: {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) return True def ip4set(card, ipaddr, *argv): @@ -434,7 +431,7 @@ def ip4set(card, ipaddr, *argv): :param card: Card object :param ipaddr: ip address to set :param argv: ioctl socket at argv[0] (or empty) - :returns: True on success + :returns: the new ip address NOTE: setting the ip will set netmask and broadcast accordingly """ if not _validip4_(ipaddr): raise pyric.error(errno.EINVAL, "Invalid ip4 address") @@ -452,10 +449,10 @@ def ip4set(card, ipaddr, *argv): return _hex2ip4_(ipaddr) else: raise pyric.error(errno.EAFNOSUPPORT, "Returned ip family is invalid") - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "ifreq error: {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def netmaskset(card, netmask, *argv): """ @@ -464,7 +461,7 @@ def netmaskset(card, netmask, *argv): :param card: Card object :param netmask: netmask to set :param argv: ioctl socket at argv[0] (or empty) - :returns: True on success + :returns: the new netmask NOTE: 1) throws error if netmask is set and card does not have an ip assigned """ @@ -490,10 +487,10 @@ def netmaskset(card, netmask, *argv): raise pyric.error(errno.EINVAL, "Cannot set netmask. Set ip first") else: raise - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "ifreq error: {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def broadcastset(card, broadcast, *argv): """ @@ -502,7 +499,7 @@ def broadcastset(card, broadcast, *argv): :param card: Card object :param broadcast: netmask to set :param argv: ioctl socket at argv[0] (or empty) - :returns: True on success + :returns: the new broadcast address NOTE: 1) throws error if netmask is set and card does not have an ip assigned 2) can set broadcast to erroneous values i.e. ipaddr = 192.168.1.2 and @@ -530,12 +527,14 @@ def broadcastset(card, broadcast, *argv): raise pyric.error(errno.EINVAL, "Cannot set broadcast. Set ip first") else: raise - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "ifreq error: {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error: {0}".format(e)) +################################################################################ #### ON/OFF #### +################################################################################ def up(card, *argv): """ @@ -554,8 +553,8 @@ def up(card, *argv): flags = _flagsget_(card.dev, iosock) if not _issetf_(flags, ifh.IFF_UP): _flagsset_(card.dev, _setf_(flags, ifh.IFF_UP), iosock) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return True def down(card, *argv): @@ -575,8 +574,8 @@ def down(card, *argv): flags = _flagsget_(card.dev, iosock) if _issetf_(flags, ifh.IFF_UP): _flagsset_(card.dev, _unsetf_(flags, ifh.IFF_UP), iosock) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return True def block(card): @@ -601,7 +600,11 @@ def unblock(card): except AttributeError: raise pyric.error(errno.ENODEV, "Device no longer registered") -def getpwrsave(card, *argv): +################################################################################ +#### RADIO PROPERTIES #### +################################################################################ + +def pwrsaveget(card, *argv): """ returns card's power save state :param card: Card object @@ -611,7 +614,7 @@ def getpwrsave(card, *argv): try: nlsock = argv[0] except IndexError: - return _nlstub_(getpwrsave, card) + return _nlstub_(pwrsaveget, card) try: msg = nl.nlmsg_new(nltype=_familyid_(nlsock), @@ -620,23 +623,24 @@ def getpwrsave(card, *argv): nl.nla_put_u32(msg, card.idx, nl80211h.NL80211_ATTR_IFINDEX) nl.nl_sendmsg(nlsock, msg) rmsg = nl.nl_recvmsg(nlsock) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return nl.nla_find(rmsg, nl80211h.NL80211_ATTR_PS_STATE) == 1 -def setpwrsave(card, on, *argv): +def pwrsaveset(card, on, *argv): """ REQUIRES ROOT PRIVILEGES sets card's power save state :param card: Card object :param on: {True = on|False = off} :param argv: netlink socket at argv[0] (or empty) + sets card's power save """ try: nlsock = argv[0] except IndexError: - return _nlstub_(setpwrsave, card, on) + return _nlstub_(pwrsaveset, card, on) try: msg = nl.nlmsg_new(nltype=_familyid_(nlsock), @@ -646,12 +650,176 @@ def setpwrsave(card, on, *argv): nl.nla_put_u32(msg, int(on), nl80211h.NL80211_ATTR_PS_STATE) nl.nl_sendmsg(nlsock, msg) _ = nl.nl_recvmsg(nlsock) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except ValueError: raise pyric.error(errno.EINVAL, "Invalid parameter on") +def covclassset(card, cc, *argv): + """ + REQUIRES ROOT PRIVILEGES + sets the coverage class. The coverage class IAW IEEE Std 802.11-2012 is + defined as the Air propagation time & together with max tx power control + the BSS diamter + :param card: Card object + :param cc: coverage class 0 to 31 IAW IEEE Std 802.11-2012 Table 8-56 + :param argv: netlink socket at argv[0] (or empty) + sets card's coverage class + """ + if cc < 0 or cc> 31: + # this can work 'incorrectly' on non-int values but these will + # be caught later during conversion + raise pyric.error(errno.EINVAL, "Coverage class must be integer 0 - 31") + + try: + nlsock = argv[0] + except IndexError: + return _nlstub_(covclassset, card, cc) + + try: + msg = nl.nlmsg_new(nltype=_familyid_(nlsock), + cmd=nl80211h.NL80211_CMD_SET_WIPHY, + flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK) + nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY) + nl.nla_put_u8(msg, int(cc), nl80211h.NL80211_ATTR_WIPHY_COVERAGE_CLASS) + nl.nl_sendmsg(nlsock, msg) + _ = nl.nl_recvmsg(nlsock) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") + except ValueError: + raise pyric.error(errno.EINVAL, "Invalid parameter value for cc") + +def retryshortset(card, lim, *argv): + """ + REQUIRES ROOT PRIVILEGES + sets the short retry limit. + :param card: Card object + :param lim: max # of short retries 1 - 255 + :param argv: netlink socket at argv[0] (or empty) + sets card's shorty retry + """ + if lim < 0 or lim > 255: + # this can work 'incorrectly' on non-int values but these will + # be caught later during conversion + raise pyric.error(errno.EINVAL, "Retry short must be integer 0 - 255") + + try: + nlsock = argv[0] + except IndexError: + return _nlstub_(retryshortset, card, lim) + + try: + msg = nl.nlmsg_new(nltype=_familyid_(nlsock), + cmd=nl80211h.NL80211_CMD_SET_WIPHY, + flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK) + nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY) + nl.nla_put_u8(msg, int(lim), nl80211h.NL80211_ATTR_WIPHY_RETRY_SHORT) + nl.nl_sendmsg(nlsock, msg) + _ = nl.nl_recvmsg(nlsock) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") + except ValueError: + raise pyric.error(errno.EINVAL, "Invalid parameter value for lim") + +def retrylongset(card, lim, *argv): + """ + REQUIRES ROOT PRIVILEGES + sets the long retry limit. + :param card: Card object + :param lim: max # of short retries 1 - 255 + :param argv: netlink socket at argv[0] (or empty) + sets card's long retry + """ + if lim < 0 or lim > 255: + # this can work 'incorrectly' on non-int values but these will + # be caught later during conversion + raise pyric.error(errno.EINVAL, "Retry long must be integer 0 - 255") + + try: + nlsock = argv[0] + except IndexError: + return _nlstub_(retrylongset, card, lim) + + try: + msg = nl.nlmsg_new(nltype=_familyid_(nlsock), + cmd=nl80211h.NL80211_CMD_SET_WIPHY, + flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK) + nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY) + nl.nla_put_u8(msg, int(lim), nl80211h.NL80211_ATTR_WIPHY_RETRY_LONG) + nl.nl_sendmsg(nlsock, msg) + _ = nl.nl_recvmsg(nlsock) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") + except ValueError: + raise pyric.error(errno.EINVAL, "Invalid parameter value for lim") + +def rtsthreshset(card, thresh, *argv): + """ + REQUIRES ROOT PRIVILEGES + sets the RTS threshold. If off, RTS is disabled. If an integer, sets the + smallest packet for which card will send an RTS prior to each transmission + :param card: Card object + :param thresh: rts threshold limit + :param argv: netlink socket at argv[0] (or empty) + sets the card's RTS threshold + """ + if thresh == 'off': thresh = _MAXTHRESH_ + else: + if thresh != _MAXTHRESH_ and (thresh < 0 or thresh > 65536): + raise pyric.error(errno.EINVAL, "Threshold must be 'off' or an integer 0 - 65536") + + try: + nlsock = argv[0] + except IndexError: + return _nlstub_(rtsthreshset, card, thresh) + + try: + msg = nl.nlmsg_new(nltype=_familyid_(nlsock), + cmd=nl80211h.NL80211_CMD_SET_WIPHY, + flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK) + nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY) + nl.nla_put_u32(msg, thresh, nl80211h.NL80211_ATTR_WIPHY_RTS_THRESHOLD) + nl.nl_sendmsg(nlsock, msg) + _ = nl.nl_recvmsg(nlsock) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") + except ValueError: + raise pyric.error(errno.EINVAL, "Invalid parameter value for thresh") + +def fragthreshset(card, thresh, *argv): + """ + REQUIRES ROOT PRIVILEGES + sets the Frag threshold. If off, fragmentation is disabled. If an integer, + sets the largest packet before the card will enable fragmentation + :param card: Card object + :param thresh: frag threshold limit in octets + :param argv: netlink socket at argv[0] (or empty) + sets the card's Fragmentation threshold + """ + if thresh == 'off': thresh = _MAXTHRESH_ + else: + if thresh != _MAXTHRESH_ and (thresh < 256 or thresh > 8000): + raise pyric.error(errno.EINVAL, "Threshold must be 'off' or an integer 256 - 8000") + + try: + nlsock = argv[0] + except IndexError: + return _nlstub_(fragthreshset, card, thresh) + + try: + msg = nl.nlmsg_new(nltype=_familyid_(nlsock), + cmd=nl80211h.NL80211_CMD_SET_WIPHY, + flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK) + nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY) + nl.nla_put_u32(msg, thresh, nl80211h.NL80211_ATTR_WIPHY_FRAG_THRESHOLD) + nl.nl_sendmsg(nlsock, msg) + _ = nl.nl_recvmsg(nlsock) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") + +################################################################################ #### INFO RELATED #### +################################################################################ def devfreqs(card, *argv): """ @@ -700,11 +868,11 @@ def devstds(card, *argv): stds = stds[:stds.find('\x00')] # remove nulls stds = stds.replace('IEEE 802.11', '') # remove IEEE 802.11 return [std for std in stds] - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except IndexError: return None except struct.error as e: - raise pyric.error(pyric.EUNDEF, "Error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def devmodes(card, *argv): """ @@ -812,8 +980,8 @@ def phyinfo(card, *argv): nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY) nl.nl_sendmsg(nlsock, msg) rmsg = nl.nl_recvmsg(nlsock) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") # pull out attributes info = {'generation':None, 'retry_short':None, 'retry_long':None, @@ -824,7 +992,9 @@ def phyinfo(card, *argv): info['retry_short'] = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_WIPHY_RETRY_SHORT) info['retry_long'] = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_WIPHY_RETRY_LONG) info['frag_thresh'] = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_WIPHY_FRAG_THRESHOLD) + if info['frag_thresh'] >= _MAXTHRESH_: info['frag_thresh'] = 'off' info['rts_thresh'] = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_WIPHY_RTS_THRESHOLD) + if info['rts_thresh'] >= _MAXTHRESH_: info['rts_thresh'] = 'off' info['cov_class'] = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_WIPHY_COVERAGE_CLASS) info['scan_ssids'] = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_MAX_NUM_SCAN_SSIDS) @@ -868,7 +1038,9 @@ def phyinfo(card, *argv): info['commands'].append("unknown cmd ({0})".format(c)) return info +################################################################################ #### TX/RX RELATED #### +################################################################################ def txset(card, lvl, pwr, *argv): """ @@ -903,8 +1075,8 @@ def txset(card, lvl, pwr, *argv): nl.nl_recvmsg(nlsock) except ValueError: raise pyric.error(errno.EINVAL, "Invalid txpwr {0}".format(pwr)) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") def txget(card, *argv): """ @@ -924,12 +1096,12 @@ def txget(card, *argv): flag = sioch.SIOCGIWTXPOW ret = io.io_transfer(iosock, flag, ifh.ifreq(card.dev, flag)) return struct.unpack_from(ifh.ifr_iwtxpwr, ret, ifh.IFNAMELEN)[0] - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except IndexError: return None except struct.error as e: - raise pyric.error(pyric.EUNDEF, "Error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def chget(card, *argv): """ @@ -997,8 +1169,8 @@ def freqset(card, rf, chw=None, *argv): nl80211h.NL80211_ATTR_WIPHY_CHANNEL_TYPE) nl.nl_sendmsg(nlsock, msg) nl.nl_recvmsg(nlsock) - except AttributeError as e: - raise pyric.error(errno.EINVAL,"Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL,"Invalid Card object") return True #### INTERFACE & MODE RELATED #### @@ -1056,8 +1228,8 @@ def modeset(card, mode, flags=None, *argv): nl80211h.NL80211_ATTR_MNTR_FLAGS) nl.nl_sendmsg(nlsock, msg) nl.nl_recvmsg(nlsock) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return True def ifaces(card, *argv): @@ -1079,8 +1251,8 @@ def ifaces(card, *argv): try: if info['card'].phy == card.phy: ifs.append((info['card'], info['mode'])) - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return ifs def devadd(card, vdev, mode, flags=None, *argv): @@ -1124,8 +1296,8 @@ def devadd(card, vdev, mode, flags=None, *argv): nl80211h.NL80211_ATTR_MNTR_FLAGS) nl.nl_sendmsg(nlsock, msg) rmsg = nl.nl_recvmsg(nlsock) # success returns new device attributes - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return Card(card.phy, vdev, nl.nla_find(rmsg, nl80211h.NL80211_ATTR_IFINDEX)) def devdel(card, *argv): @@ -1150,8 +1322,8 @@ def devdel(card, *argv): nl.nla_put_u32(msg, card.idx, nl80211h.NL80211_ATTR_IFINDEX) nl.nl_sendmsg(nlsock, msg) nl.nl_recvmsg(nlsock) - except AttributeError as e: - raise pyric.error(errno.EINVAL,"Invalid paramter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") return True ################################################################################ @@ -1236,10 +1408,10 @@ def _flagsget_(dev, *argv): flag = sioch.SIOCGIFFLAGS ret = io.io_transfer(iosock, flag, ifh.ifreq(dev, flag)) return struct.unpack_from(ifh.ifr_flags, ret, ifh.IFNAMELEN)[0] - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def _flagsset_(dev, flags, *argv): """ @@ -1258,10 +1430,10 @@ def _flagsset_(dev, flags, *argv): flag = sioch.SIOCSIFFLAGS ret = io.io_transfer(iosock, flag, ifh.ifreq(dev, flag, [flags])) return struct.unpack_from(ifh.ifr_flags, ret, ifh.IFNAMELEN)[0] - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) #### ADDITIONAL PARSING FOR PHYINFO #### @@ -1329,10 +1501,10 @@ def _ifindex_(dev, *argv): flag = sioch.SIOCGIFINDEX ret = io.io_transfer(iosock, flag, ifh.ifreq(dev, flag)) return struct.unpack_from(ifh.ifr_ifindex, ret, ifh.IFNAMELEN)[0] - except AttributeError as e: - raise pyric.error(errno.EINVAL, "Invalid parameter {0}".format(e)) + except AttributeError: + raise pyric.error(errno.EINVAL, "Invalid Card object") except struct.error as e: - raise pyric.error(pyric.EUNDEF, "error parsing results {0}".format(e)) + raise pyric.error(pyric.EUNDEF, "Ifreq error {0}".format(e)) def _familyid_(nlsock): """ diff --git a/setup.py b/setup.py index ae73e57..b7db6b4 100644 --- a/setup.py +++ b/setup.py @@ -74,7 +74,7 @@ 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7'], - keywords='Linux nl80211 iw wireless pentest', + keywords='Linux Python nl80211 iw iwconfig ifconfig wireless WLAN WiFi pentest', packages=find_packages(), package_data={'pyric':['nlhelp/*.help','utils/data/*.txt']} )