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 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 _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']