def callback_dump(msg, results): """Here is where SSIDs and their data is decoded from the binary data sent by the kernel. This function is called once per SSID. Everything in `msg` pertains to just one SSID. Positional arguments: msg -- nl_msg class instance containing the data sent by the kernel. results -- dictionary to populate with parsed data. """ bss = dict() # To be filled by nla_parse_nested(). # First we must parse incoming data into manageable chunks and check for errors. gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) tb = dict((i, None) for i in range(nl80211.NL80211_ATTR_MAX + 1)) nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) if not tb[nl80211.NL80211_ATTR_BSS]: print('WARNING: BSS info missing for an access point.') return libnl.handlers.NL_SKIP if nla_parse_nested(bss, nl80211.NL80211_BSS_MAX, tb[nl80211.NL80211_ATTR_BSS], bss_policy): print('WARNING: Failed to parse nested attributes for an access point!') return libnl.handlers.NL_SKIP if not bss[nl80211.NL80211_BSS_BSSID]: print('WARNING: No BSSID detected for an access point!') return libnl.handlers.NL_SKIP if not bss[nl80211.NL80211_BSS_INFORMATION_ELEMENTS]: print('WARNING: No additional information available for an access point!') return libnl.handlers.NL_SKIP # Further parse and then store. Overwrite existing data for BSSID if scan is run multiple times. bss_parsed = parse_bss(bss) results[bss_parsed['bssid']] = bss_parsed return libnl.handlers.NL_SKIP
def _callback_dump(self, msg, results): # Here is where SSIDs and their data is decoded from the binary data # sent by the kernel. This function is called once per SSID. Everything # in `msg` pertains to just one SSID. # # Positional arguments: # msg -- nl_msg class instance containing the data sent by the kernel. # results -- dictionary to populate with parsed data. bss = dict() # To be filled by nla_parse_nested(). # First we must parse incoming data into manageable chunks and check for errors. gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) tb = dict((i, None) for i in range(nl80211.NL80211_ATTR_MAX + 1)) nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) if not tb[nl80211.NL80211_ATTR_BSS]: logger.warning('BSS info missing for an access point.') return libnl.handlers.NL_SKIP if nla_parse_nested(bss, nl80211.NL80211_BSS_MAX, tb[nl80211.NL80211_ATTR_BSS], bss_policy): logger.warning( 'Failed to parse nested attributes for an access point!') return libnl.handlers.NL_SKIP if not bss[nl80211.NL80211_BSS_BSSID]: logger.warning('No BSSID detected for an access point!') return libnl.handlers.NL_SKIP if not bss[nl80211.NL80211_BSS_INFORMATION_ELEMENTS]: logger.warning( 'No additional information available for an access point!') return libnl.handlers.NL_SKIP # Further parse and then store. Overwrite existing data for BSSID if scan is run multiple times. bss_parsed = parse_bss(bss) results[bss_parsed['bssid']] = bss_parsed return libnl.handlers.NL_SKIP
def dump_callback(msg, _): gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) tb = dict((i, None) for i in range(NCSI_ATTR_MAX + 1)) nla_parse(tb, NCSI_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) print(tb) return NL_SKIP
def callback_trigger(self, msg, arg): gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) tb = dict((i, None) for i in range(10 + 1)) nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) if tb.get(NL_CMD_RECOVERY_MSG): self.recovery = True return libnl.handlers.NL_STOP
def callback(msg, has_printed): """Callback function called by libnl upon receiving messages from the kernel. Positional arguments: msg -- nl_msg class instance containing the data sent by the kernel. has_printed -- simple pseudo boolean (if list is empty) to keep track of when to print emtpy lines. Returns: An integer, value of NL_SKIP. It tells libnl to stop calling other callbacks for this message and proceed with processing the next kernel message. """ table = AsciiTable([['Data Type', 'Data Value']]) # First convert `msg` into something more manageable. gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) # Partially parse the raw binary data and place them in the `tb` dictionary. tb = dict( (i, None) for i in range(nl80211.NL80211_ATTR_MAX + 1)) # Need to populate dict with all possible keys. nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) # Now it's time to grab the juicy data! if tb[nl80211.NL80211_ATTR_IFNAME]: table.title = nla_get_string( tb[nl80211.NL80211_ATTR_IFNAME]).decode('ascii') else: table.title = 'Unnamed Interface' if tb[nl80211.NL80211_ATTR_WIPHY]: wiphy_num = nla_get_u32(tb[nl80211.NL80211_ATTR_WIPHY]) wiphy = ('wiphy {0}' if OPTIONS['<interface>'] else 'phy#{0}').format(wiphy_num) table.table_data.append(['NL80211_ATTR_WIPHY', wiphy]) if tb[nl80211.NL80211_ATTR_MAC]: mac_address = ':'.join( format(x, '02x') for x in nla_data(tb[nl80211.NL80211_ATTR_MAC])[:6]) table.table_data.append(['NL80211_ATTR_MAC', mac_address]) if tb[nl80211.NL80211_ATTR_IFINDEX]: table.table_data.append([ 'NL80211_ATTR_IFINDEX', str(nla_get_u32(tb[nl80211.NL80211_ATTR_IFINDEX])) ]) # Print all data. if has_printed: print() else: has_printed.append(True) print(table.table) return NL_SKIP
def callback(msg, has_printed): """Callback function called by libnl upon receiving messages from the kernel. Positional arguments: msg -- nl_msg class instance containing the data sent by the kernel. has_printed -- simple pseudo boolean (if list is empty) to keep track of when to print emtpy lines. Returns: An integer, value of NL_SKIP. It tells libnl to stop calling other callbacks for this message and proceed with processing the next kernel message. """ table = AsciiTable([['Data Type', 'Data Value']]) # First convert `msg` into something more manageable. gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) # Partially parse the raw binary data and place them in the `tb` dictionary. tb = dict((i, None) for i in range(nl80211.NL80211_ATTR_MAX + 1)) # Need to populate dict with all possible keys. nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) # Now it's time to grab the juicy data! if tb[nl80211.NL80211_ATTR_IFNAME]: table.title = nla_get_string(tb[nl80211.NL80211_ATTR_IFNAME]).decode('ascii') else: table.title = 'Unnamed Interface' if tb[nl80211.NL80211_ATTR_WIPHY]: wiphy_num = nla_get_u32(tb[nl80211.NL80211_ATTR_WIPHY]) wiphy = ('wiphy {0}' if OPTIONS['<interface>'] else 'phy#{0}').format(wiphy_num) table.table_data.append(['NL80211_ATTR_WIPHY', wiphy]) if tb[nl80211.NL80211_ATTR_MAC]: mac_address = ':'.join(format(x, '02x') for x in nla_data(tb[nl80211.NL80211_ATTR_MAC])[:6]) table.table_data.append(['NL80211_ATTR_MAC', mac_address]) if tb[nl80211.NL80211_ATTR_IFINDEX]: table.table_data.append(['NL80211_ATTR_IFINDEX', str(nla_get_u32(tb[nl80211.NL80211_ATTR_IFINDEX]))]) # Print all data. if has_printed: print() else: has_printed.append(True) print(table.table) return NL_SKIP
def _iface_callback(self, msg, _): # Callback function called by libnl upon receiving messages from the # kernel. # # Positional arguments: # msg -- nl_msg class instance containing the data sent by the kernel. # # Returns: # An integer, value of NL_SKIP. It tells libnl to stop calling other # callbacks for this message and proceed with processing the next kernel # message. # First convert `msg` into something more manageable. gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) # Partially parse the raw binary data and place them in the `tb` # dictionary. Need to populate dict with all possible keys. tb = dict((i, None) for i in range(nl80211.NL80211_ATTR_MAX + 1)) nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) # Now it's time to grab the data, we start with the interface index as # universal identifier if tb[nl80211.NL80211_ATTR_IFINDEX]: if_index = nla_get_u32(tb[nl80211.NL80211_ATTR_IFINDEX]) else: return NL_SKIP # Create new interface dict if this interface is not yet known if if_index in self.iface_data: iface_data = self.iface_data[if_index] else: iface_data = {} if tb[nl80211.NL80211_ATTR_IFNAME]: iface_data['name'] = nla_get_string( tb[nl80211.NL80211_ATTR_IFNAME]).decode('ascii') if tb[nl80211.NL80211_ATTR_IFTYPE]: iftype = nla_get_u32(tb[nl80211.NL80211_ATTR_IFTYPE]) if iftype == nl80211.NL80211_IFTYPE_UNSPECIFIED: typestr = 'UNSPECIFIED' elif iftype == nl80211.NL80211_IFTYPE_ADHOC: typestr = 'ADHOC' elif iftype == nl80211.NL80211_IFTYPE_STATION: typestr = 'STATION' elif iftype == nl80211.NL80211_IFTYPE_AP: typestr = 'AP' elif iftype == nl80211.NL80211_IFTYPE_AP_VLAN: typestr = 'AP_VLAN' elif iftype == nl80211.NL80211_IFTYPE_WDS: typestr = 'WDS' elif iftype == nl80211.NL80211_IFTYPE_MONITOR: typestr = 'MONITOR' elif iftype == nl80211.NL80211_IFTYPE_MESH_POINT: typestr = 'MESH_POINT' elif iftype == nl80211.NL80211_IFTYPE_P2P_CLIENT: typestr = 'P2P_CLIENT' elif iftype == nl80211.NL80211_IFTYPE_P2P_GO: typestr = 'P2P_GO' elif iftype == nl80211.NL80211_IFTYPE_P2P_DEVICE: typestr = 'P2P_DEVICE' iface_data['type'] = typestr if tb[nl80211.NL80211_ATTR_WIPHY]: wiphy_num = nla_get_u32(tb[nl80211.NL80211_ATTR_WIPHY]) iface_data['wiphy'] = 'phy#{0}'.format(wiphy_num) if tb[nl80211.NL80211_ATTR_MAC]: mac_raw = nla_data(tb[nl80211.NL80211_ATTR_MAC])[:6] mac_address = ':'.join(format(x, '02x') for x in mac_raw) iface_data['mac'] = mac_address if (gnlh.cmd == nl80211.NL80211_CMD_NEW_STATION and if_index == self.if_idx): # This is the BSSID that we're currently associated to self.bssid = mac_address if tb[nl80211.NL80211_ATTR_GENERATION]: generation = nla_get_u32(tb[nl80211.NL80211_ATTR_GENERATION]) # Do not overwrite the generation for excessively large values if generation < 100: iface_data['generation'] = generation if tb[nl80211.NL80211_ATTR_WIPHY_TX_POWER_LEVEL]: iface_data['tx_power'] = nla_get_u32( tb[nl80211.NL80211_ATTR_WIPHY_TX_POWER_LEVEL]) / 100 # mW if tb[nl80211.NL80211_ATTR_CHANNEL_WIDTH]: iface_data['ch_width'] = nla_get_u32( tb[nl80211.NL80211_ATTR_CHANNEL_WIDTH]) if tb[nl80211.NL80211_ATTR_CENTER_FREQ1]: iface_data['frequency'] = nla_get_u32( tb[nl80211.NL80211_ATTR_CENTER_FREQ1]) # Station infos if tb[nl80211.NL80211_ATTR_STA_INFO]: # Need to unpack the data sinfo = dict( (i, None) for i in range(nl80211.NL80211_STA_INFO_MAX)) rinfo = dict( (i, None) for i in range(nl80211.NL80211_STA_INFO_TX_BITRATE)) # Extract data nla_parse_nested(sinfo, nl80211.NL80211_STA_INFO_MAX, tb[nl80211.NL80211_ATTR_STA_INFO], None) # Extract info about signal strength (= quality) if sinfo[nl80211.NL80211_STA_INFO_SIGNAL]: iface_data['signal'] = 100 + \ nla_get_u8(sinfo[nl80211.NL80211_STA_INFO_SIGNAL]) # Compute quality (formula found in iwinfo_nl80211.c and largely # simplified) iface_data['quality'] = iface_data['signal'] + 110 iface_data['quality_max'] = 70 # Extract info about negotiated bitrate if sinfo[nl80211.NL80211_STA_INFO_TX_BITRATE]: nla_parse_nested(rinfo, nl80211.NL80211_RATE_INFO_MAX, sinfo[nl80211.NL80211_STA_INFO_TX_BITRATE], None) if rinfo[nl80211.NL80211_RATE_INFO_BITRATE]: iface_data['bitrate'] = nla_get_u16( rinfo[nl80211.NL80211_RATE_INFO_BITRATE]) / 10 # BSS info if tb[nl80211.NL80211_ATTR_BSS]: # Need to unpack the data binfo = dict((i, None) for i in range(nl80211.NL80211_BSS_MAX)) nla_parse_nested(binfo, nl80211.NL80211_BSS_MAX, tb[nl80211.NL80211_ATTR_BSS], None) # Parse BSS section (if complete) try: bss = parse_bss(binfo) # Remove duplicated information blocks if 'beacon_ies' in bss: del bss['beacon_ies'] if 'information_elements' in bss: del bss['information_elements'] if 'supported_rates' in bss: del bss['supported_rates'] # Convert timedelta objects for later JSON encoding for prop in bss: if isinstance(bss[prop], datetime.timedelta): bss[prop] = int(bss[prop].microseconds) / 1000 # Append BSS data to general object iface_data = {**iface_data, **bss} except Exception as e: logger.warning("Obtaining BSS data failed: {}".format(e)) pass # Append data to global structure self.iface_data[if_index] = iface_data return NL_SKIP
def test_no_security(): """iw output to test against. BSS 00:0d:67:23:b8:46(on wlan0) TSF: 1680943821184 usec (19d, 10:55:43) freq: 2412 beacon interval: 100 TUs capability: ESS ShortPreamble ShortSlotTime (0x0421) signal: -66.00 dBm last seen: 4630 ms ago Information elements from Probe Response frame: SSID: CableWiFi Supported rates: 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0 24.0 DS Parameter set: channel 1 TIM: DTIM Count 0 DTIM Period 1 Bitmap Control 0x0 Bitmap[0] 0x0 Country: US Environment: Outdoor only Channels [1 - 11] @ 36 dBm ERP: <no flags> Extended supported rates: 36.0 48.0 54.0 HT capabilities: Capabilities: 0x2d RX LDPC HT20 SM Power Save disabled RX HT20 SGI No RX STBC Max AMSDU length: 3839 bytes No DSSS/CCK HT40 Maximum RX AMPDU length 65535 bytes (exponent: 0x003) Minimum RX AMPDU time spacing: No restriction (0x00) HT RX MCS rate indexes supported: 0-23 HT TX MCS rate indexes are undefined HT operation: * primary channel: 1 * secondary channel offset: no secondary * STA channel width: 20 MHz * RIFS: 0 * HT protection: nonmember * non-GF present: 0 * OBSS non-GF present: 0 * dual beacon: 0 * dual CTS protection: 0 * STBC beacon: 0 * L-SIG TXOP Prot: 0 * PCO active: 0 * PCO phase: 0 Overlapping BSS scan params: * passive dwell: 20 TUs * active dwell: 10 TUs * channel width trigger scan interval: 300 s * scan passive total per channel: 200 TUs * scan active total per channel: 20 TUs * BSS width channel transition delay factor: 5 * OBSS Scan Activity Threshold: 0.25 % Extended capabilities: HT Information Exchange Supported, SSID List WMM: * Parameter version 1 * BE: CW 15-1023, AIFSN 3 * BK: CW 15-1023, AIFSN 7 * VI: CW 7-15, AIFSN 2, TXOP 3008 usec * VO: CW 3-7, AIFSN 2, TXOP 1504 usec """ data = bytearray(base64.b64decode( b'IgEAAAgALgCJnxAACAADAAgAAAAMAJkAAQAAAAMAAACgAS8ACgABAAANZyO4RgAADAADAIAxD2CHAQAAowAGAAAJQ2FibGVXaUZpAQiEi5YME' b'hgkMAMBAQUEAAEAAAcGVVNPAQskKgEAMgNIYGwtGi0AA////wAAAAAAAAAAAAAAAAAABAbm5w0APRYBAAEAAAAAAAAAAAAAAAAAAAAAAAAASg' b'4UAAoALAHIABQABQAZAH8GAQAAAgAA3RgAUPICAQEHAAOkAAAnpAAAQkNeAGIyLwDdCQADfwEBAAD/fwAMAA0AgDEPYIcBAACjAAsAAAlDYWJ' b'sZVdpRmkBCISLlgwSGCQwAwEBBQQAAQAABwZVU08BCyQqAQAyA0hgbC0aLQAD////AAAAAAAAAAAAAAAAAAAEBubnDQA9FgEAAQAAAAAAAAAA' b'AAAAAAAAAAAAAABKDhQACgAsAcgAFAAFABkAfwYBAAACAADdGABQ8gIBAQcAA6QAACekAABCQ14AYjIvAN0JAAN/AQEAAP9/AAYABABkAAAAB' b'gAFACEEAAAIAAIAbAkAAAgADAAAAAAACAAKAGwgAAAIAAcAOOb//w==' )) gnlh = genlmsghdr(data) tb = dict((i, None) for i in range(NL80211_ATTR_MAX + 1)) nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) bss = dict() nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy) bss_parsed = parse_bss(bss) assert '00:0d:67:23:b8:46' == bss_parsed['bssid'] assert timedelta(microseconds=1680943821184) == bss_parsed['tsf'] assert 2412 == bss_parsed['frequency'] assert 100 == bss_parsed['beacon_interval'] assert ['ESS', 'ShortPreamble', 'ShortSlotTime'] == sorted(bss_parsed['capability']) assert -66.0 == bss_parsed['signal'] assert timedelta(milliseconds=4630) < bss_parsed['seen_ms_ago'] < timedelta(milliseconds=9999) assert u'CableWiFi' == bss_parsed['ssid'] assert ['11.0*', '12.0', '18.0', '2.0*', '24.0', '5.5*', '6.0', '9.0'] == sorted(bss_parsed['supported_rates']) assert 1 == bss_parsed['channel'] assert ['36.0', '48.0', '54.0'] == sorted(bss_parsed['extended_supported_rates']) assert 20 == bss_parsed['channel_width']
def getStationInfo_callback(msg, results): # Dictionnary later populated with response message sub-attributes sinfo = dict() # Get the header of the message gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg))) tb = dict((i, None) for i in range(nl80211.NL80211_ATTR_MAX + 1)) # Define the data structure of the netlink attributes we will receive stats_policy = dict( (i, None) for i in range(nl80211.NL80211_STA_INFO_MAX + 1)) stats_policy.update({ nl80211.NL80211_STA_INFO_INACTIVE_TIME: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_RX_BYTES: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_TX_BYTES: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_RX_PACKETS: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_TX_PACKETS: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_SIGNAL: nla_policy(type_=NLA_U8), nl80211.NL80211_STA_INFO_SIGNAL_AVG: nla_policy(type_=NLA_U8), nl80211.NL80211_STA_INFO_T_OFFSET: nla_policy(type_=NLA_U64), nl80211.NL80211_STA_INFO_TX_BITRATE: nla_policy(type_=NLA_NESTED), nl80211.NL80211_STA_INFO_RX_BITRATE: nla_policy(type_=NLA_NESTED), nl80211.NL80211_STA_INFO_LLID: nla_policy(type_=NLA_U16), nl80211.NL80211_STA_INFO_PLID: nla_policy(type_=NLA_U16), nl80211.NL80211_STA_INFO_PLINK_STATE: nla_policy(type_=NLA_U8), nl80211.NL80211_STA_INFO_TX_RETRIES: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_TX_FAILED: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_LOCAL_PM: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_PEER_PM: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_NONPEER_PM: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_CHAIN_SIGNAL: nla_policy(type_=NLA_NESTED), nl80211.NL80211_STA_INFO_RX_BYTES64: nla_policy(type_=NLA_U64), nl80211.NL80211_STA_INFO_TX_BYTES64: nla_policy(type_=NLA_U64), nl80211.NL80211_STA_INFO_BEACON_LOSS: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_CONNECTED_TIME: nla_policy(type_=NLA_U32), nl80211.NL80211_STA_INFO_BSS_PARAM: nla_policy(type_=NLA_NESTED), }) # If any value in the stats_policy is empty, pad it with a default NLA_U8 type to avoid # any issue during validation for key in stats_policy: if stats_policy[key] is None: stats_policy[key] = nla_policy(type_=NLA_U8) # Parse the stream of attributes received into indexed chunks of data nla_parse(tb, nl80211.NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), None) # If we haven't received Station info data, don't go further and skip this message if tb[nl80211.NL80211_ATTR_STA_INFO] is None: return libnl.handlers.NL_SKIP # Finally, feed the attributes of the message into the chunk defined before nla_parse_nested(sinfo, nl80211.NL80211_STA_INFO_MAX, tb[nl80211.NL80211_ATTR_STA_INFO], stats_policy) # Create the Station object station = Station() # Finally, if an attribute of interest is present, save it in the object if tb[nl80211.NL80211_ATTR_MAC]: # Convert the station MAC address to something human readable raw_mac = nla_get_string(tb[nl80211.NL80211_ATTR_MAC]) if len(raw_mac) == 6: station.mac_addr = "%x:%x:%x:%x:%x:%x" % struct.unpack( "BBBBBB", raw_mac) if sinfo[nl80211.NL80211_STA_INFO_RX_BYTES]: station.rx_bytes = nla_get_u32( sinfo[nl80211.NL80211_STA_INFO_RX_BYTES]) if sinfo[nl80211.NL80211_STA_INFO_TX_BYTES]: station.tx_bytes = nla_get_u32( sinfo[nl80211.NL80211_STA_INFO_TX_BYTES]) if sinfo[nl80211.NL80211_STA_INFO_RX_PACKETS]: station.rx_packets = nla_get_u32( sinfo[nl80211.NL80211_STA_INFO_RX_PACKETS]) if sinfo[nl80211.NL80211_STA_INFO_TX_PACKETS]: station.tx_packets = nla_get_u32( sinfo[nl80211.NL80211_STA_INFO_TX_PACKETS]) if sinfo[nl80211.NL80211_STA_INFO_TX_FAILED]: station.tx_failed = nla_get_u32( sinfo[nl80211.NL80211_STA_INFO_TX_FAILED]) if sinfo[nl80211.NL80211_STA_INFO_SIGNAL]: # Signal level is saved as an 8-bit byte, so we convert it to a signed integer raw_signal = nla_get_u8(sinfo[nl80211.NL80211_STA_INFO_SIGNAL]) if raw_signal > 127: station.signal = raw_signal - 256 else: station.signal = raw_signal # Append the station to the list of station and iterate to the next result results.append(station) return libnl.handlers.NL_SKIP