def do_scan_trigger(sk, if_index, driver_id, mcid): """Issue a scan request to the kernel and wait for it to reply with a signal. This function issues NL80211_CMD_TRIGGER_SCAN which requires root privileges. The way NL80211 works is first you issue NL80211_CMD_TRIGGER_SCAN and wait for the kernel to signal that the scan is done. When that signal occurs, data is not yet available. The signal tells us if the scan was aborted or if it was successful (if new scan results are waiting). This function handles that simple signal. May exit the program (sys.exit()) if a fatal error occurs. Positional arguments: sk -- nl_sock class instance (from nl_socket_alloc()). if_index -- interface index (integer). driver_id -- nl80211 driver ID from genl_ctrl_resolve() (integer). mcid -- nl80211 scanning group ID from genl_ctrl_resolve_grp() (integer). Returns: 0 on success or a negative error code. """ # First get the "scan" membership group ID and join the socket to the group. ret = nl_socket_add_membership( sk, mcid) # Listen for results of scan requests (aborted or new results). if ret < 0: return ret # Build the message to be sent to the kernel. msg = nlmsg_alloc() genlmsg_put(msg, 0, 0, driver_id, 0, 0, nl80211.NL80211_CMD_TRIGGER_SCAN, 0) # Setup which command to run. nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index) # Setup which interface to use. ssids_to_scan = nlmsg_alloc() nla_put(ssids_to_scan, 1, 0, b'') # Scan all SSIDs. nla_put_nested(msg, nl80211.NL80211_ATTR_SCAN_SSIDS, ssids_to_scan) # Setup what kind of scan to perform. # Setup the callbacks to be used for triggering the scan only. err = ctypes.c_int( 1 ) # Used as a mutable integer to be updated by the callback function. Signals end of messages. results = ctypes.c_int( -1 ) # Signals if the scan was successful (new results) or aborted, or not started. cb = libnl.handlers.nl_cb_alloc(libnl.handlers.NL_CB_DEFAULT) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_VALID, libnl.handlers.NL_CB_CUSTOM, callback_trigger, results) libnl.handlers.nl_cb_err(cb, libnl.handlers.NL_CB_CUSTOM, error_handler, err) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_ACK, libnl.handlers.NL_CB_CUSTOM, ack_handler, err) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_SEQ_CHECK, libnl.handlers.NL_CB_CUSTOM, lambda *_: libnl.handlers.NL_OK, None) # Ignore sequence checking. # Now we send the message to the kernel, and retrieve the acknowledgement. The kernel takes a few seconds to finish # scanning for access points. ret = nl_send_auto(sk, msg) if ret < 0: return ret while err.value > 0: ret = nl_recvmsgs(sk, cb) if ret < 0: return ret if err.value < 0: raise RuntimeError("Unknown Error") # Block until the kernel is done scanning or aborted the scan. while results.value < 0: ret = nl_recvmsgs(sk, cb) if ret < 0: return ret if results.value > 0: raise RuntimeError('The kernel aborted the scan.') # Done, cleaning up. return nl_socket_drop_membership( sk, mcid) # No longer need to receive multicast messages.
def _do_scan_trigger(self, if_index, driver_id, mcid): # Issue a scan request to the kernel and wait for it to reply with a # signal. # # This function issues NL80211_CMD_TRIGGER_SCAN which requires root # privileges. The way NL80211 works is first you issue # NL80211_CMD_TRIGGER_SCAN and wait for the kernel to signal that the # scan is done. When that signal occurs, data is not yet available. The # signal tells us if the scan was aborted or if it was successful (if # new scan results are waiting). This function handles that simple # signal. May exit the program (sys.exit()) if a fatal error occurs. # # Positional arguments: # self._nl_sock -- nl_sock class instance (from nl_socket_alloc()). # if_index -- interface index (integer). # driver_id -- nl80211 driver ID from genl_ctrl_resolve() (integer). # mcid -- nl80211 scanning group ID from genl_ctrl_resolve_grp() # (integer). # # Returns: # 0 on success or a negative error code. # First get the "scan" membership group ID and join the socket to the # group. logger.debug('Joining group %d.', mcid) # Listen for results of scan requests (aborted or new results). ret = nl_socket_add_membership(self._nl_sock, mcid) if ret < 0: return ret # Build the message to be sent to the kernel. msg = nlmsg_alloc() # Setup which command to run. genlmsg_put(msg, 0, 0, driver_id, 0, 0, nl80211.NL80211_CMD_TRIGGER_SCAN, 0) # Setup which interface to use. nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index) ssids_to_scan = nlmsg_alloc() nla_put(ssids_to_scan, 1, 0, b'') # Scan all SSIDs. # Setup what kind of scan to perform. nla_put_nested(msg, nl80211.NL80211_ATTR_SCAN_SSIDS, ssids_to_scan) # Setup the callbacks to be used for triggering the scan only. # Used as a mutable integer to be updated by the callback function. # Signals end of messages. err = ctypes.c_int(1) # Signals if the scan was successful (new results) or aborted, or not # started. results = ctypes.c_int(-1) cb = libnl.handlers.nl_cb_alloc(libnl.handlers.NL_CB_DEFAULT) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_VALID, libnl.handlers.NL_CB_CUSTOM, self._callback_trigger, results) libnl.handlers.nl_cb_err(cb, libnl.handlers.NL_CB_CUSTOM, self._error_handler, err) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_ACK, libnl.handlers.NL_CB_CUSTOM, self._ack_handler, err) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_SEQ_CHECK, libnl.handlers.NL_CB_CUSTOM, lambda *_: libnl.handlers.NL_OK, None) # Ignore sequence checking. # Now we send the message to the kernel, and retrieve the # acknowledgement. The kernel takes a few seconds to finish scanning for # access points. logger.debug('Sending NL80211_CMD_TRIGGER_SCAN...') ret = nl_send_auto(self._nl_sock, msg) if ret < 0: return ret while err.value > 0: logger.debug( 'Retrieving NL80211_CMD_TRIGGER_SCAN acknowledgement...') ret = nl_recvmsgs(self._nl_sock, cb) if ret < 0: return ret if err.value < 0: logger.warning('Unknown error {0} ({1})'.format( err.value, errmsg[abs(err.value)])) # Block until the kernel is done scanning or aborted the scan. while results.value < 0: logger.debug( 'Retrieving NL80211_CMD_TRIGGER_SCAN final response...') ret = nl_recvmsgs(self._nl_sock, cb) if ret < 0: return ret if results.value > 0: logger.warning('The kernel aborted the scan.') # Done, cleaning up. logger.debug('Leaving group %d.', mcid) # No longer need to receive multicast messages. return nl_socket_drop_membership(self._nl_sock, mcid)
def do_scan_trigger(sk, if_index, driver_id, mcid): """Issue a scan request to the kernel and wait for it to reply with a signal. This function issues NL80211_CMD_TRIGGER_SCAN which requires root privileges. The way NL80211 works is first you issue NL80211_CMD_TRIGGER_SCAN and wait for the kernel to signal that the scan is done. When that signal occurs, data is not yet available. The signal tells us if the scan was aborted or if it was successful (if new scan results are waiting). This function handles that simple signal. May exit the program (sys.exit()) if a fatal error occurs. Positional arguments: sk -- nl_sock class instance (from nl_socket_alloc()). if_index -- interface index (integer). driver_id -- nl80211 driver ID from genl_ctrl_resolve() (integer). mcid -- nl80211 scanning group ID from genl_ctrl_resolve_grp() (integer). Returns: 0 on success or a negative error code. """ # First get the "scan" membership group ID and join the socket to the group. _LOGGER.debug('Joining group %d.', mcid) ret = nl_socket_add_membership(sk, mcid) # Listen for results of scan requests (aborted or new results). if ret < 0: return ret # Build the message to be sent to the kernel. msg = nlmsg_alloc() genlmsg_put(msg, 0, 0, driver_id, 0, 0, nl80211.NL80211_CMD_TRIGGER_SCAN, 0) # Setup which command to run. nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index) # Setup which interface to use. ssids_to_scan = nlmsg_alloc() nla_put(ssids_to_scan, 1, 0, b'') # Scan all SSIDs. nla_put_nested(msg, nl80211.NL80211_ATTR_SCAN_SSIDS, ssids_to_scan) # Setup what kind of scan to perform. # Setup the callbacks to be used for triggering the scan only. err = ctypes.c_int(1) # Used as a mutable integer to be updated by the callback function. Signals end of messages. results = ctypes.c_int(-1) # Signals if the scan was successful (new results) or aborted, or not started. cb = libnl.handlers.nl_cb_alloc(libnl.handlers.NL_CB_DEFAULT) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_VALID, libnl.handlers.NL_CB_CUSTOM, callback_trigger, results) libnl.handlers.nl_cb_err(cb, libnl.handlers.NL_CB_CUSTOM, error_handler, err) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_ACK, libnl.handlers.NL_CB_CUSTOM, ack_handler, err) libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_SEQ_CHECK, libnl.handlers.NL_CB_CUSTOM, lambda *_: libnl.handlers.NL_OK, None) # Ignore sequence checking. # Now we send the message to the kernel, and retrieve the acknowledgement. The kernel takes a few seconds to finish # scanning for access points. _LOGGER.debug('Sending NL80211_CMD_TRIGGER_SCAN...') ret = nl_send_auto(sk, msg) if ret < 0: return ret while err.value > 0: _LOGGER.debug('Retrieving NL80211_CMD_TRIGGER_SCAN acknowledgement...') ret = nl_recvmsgs(sk, cb) if ret < 0: return ret if err.value < 0: error('Unknown error {0} ({1})'.format(err.value, errmsg[abs(err.value)])) # Block until the kernel is done scanning or aborted the scan. while results.value < 0: _LOGGER.debug('Retrieving NL80211_CMD_TRIGGER_SCAN final response...') ret = nl_recvmsgs(sk, cb) if ret < 0: return ret if results.value > 0: error('The kernel aborted the scan.') # Done, cleaning up. _LOGGER.debug('Leaving group %d.', mcid) return nl_socket_drop_membership(sk, mcid) # No longer need to receive multicast messages.