예제 #1
0
    def _do_scan_results(self, if_index, driver_id, results):
        # Retrieve the results of a successful scan (SSIDs and data about them).
        # This function does not require root privileges. It eventually calls a
        # callback that actually decodes data about SSIDs but this function
        # kicks that off. 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).
        # results -- dictionary to populate with results. Keys are BSSIDs (MAC
        #            addresses) and values are dicts of data.
        # Returns:
        # 0 on success or a negative error code.

        msg = nlmsg_alloc()
        genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP,
                    nl80211.NL80211_CMD_GET_SCAN, 0)
        nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index)
        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_dump, results)
        logger.debug('Sending NL80211_CMD_GET_SCAN...')
        ret = nl_send_auto(self._nl_sock, msg)
        if ret >= 0:
            logger.debug('Retrieving NL80211_CMD_GET_SCAN response...')
            ret = nl_recvmsgs(self._nl_sock, cb)
        return ret
예제 #2
0
def do_scan_results(sk, if_index, driver_id, results):
    """Retrieve the results of a successful scan (SSIDs and data about them).

    This function does not require root privileges. It eventually calls a callback that actually decodes data about
    SSIDs but this function kicks that off.

    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).
    results -- dictionary to populate with results. Keys are BSSIDs (MAC addresses) and values are dicts of data.

    Returns:
    0 on success or a negative error code.
    """
    msg = nlmsg_alloc()
    genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP,
                nl80211.NL80211_CMD_GET_SCAN, 0)
    nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index)
    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_dump,
                             results)
    ret = nl_send_auto(sk, msg)
    if ret >= 0:
        try:
            ret = nl_recvmsgs(sk, cb)
        except NotImplementedError:
            pass
    return ret
예제 #3
0
def ncsi_set_interface(ifindex, package, channel):

    sk = nl_socket_alloc()
    ret = genl_connect(sk)
    if ret < 0:
        return ret

    driver_id = genl_ctrl_resolve(sk, b'NCSI')
    if driver_id < 0:
        return driver_id

    msg = nlmsg_alloc()
    if package or package and channel:
        genlmsg_put(msg, 0, 0, driver_id, 0, 0, NCSI_CMD_SET_INTERFACE, 0)
        ret = nla_put_u32(msg, NCSI_ATTR_PACKAGE_ID, int(package))
        if channel:
            ret = nla_put_u32(msg, NCSI_ATTR_CHANNEL_ID, int(channel))
    else:
        genlmsg_put(msg, 0, 0, driver_id, 0, 0, NCSI_CMD_CLEAR_INTERFACE, 0)
    ret = nla_put_u32(msg, NCSI_ATTR_IFINDEX, ifindex)

    nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback, None)

    ret = nl_send_auto(sk, msg)
    if ret < 0:
        print("Failed to send message: {}".format(ret))
        return ret

    ret = nl_recvmsgs_default(sk)  # blocks
    if ret < 0:
        reason = errmsg[abs(ret)]
        print("recvmsg returned {}, {}".format(ret, reason))
예제 #4
0
def do_scan_results(sk, if_index, driver_id, results):
    """Retrieve the results of a successful scan (SSIDs and data about them).

    This function does not require root privileges. It eventually calls a callback that actually decodes data about
    SSIDs but this function kicks that off.

    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).
    results -- dictionary to populate with results. Keys are BSSIDs (MAC addresses) and values are dicts of data.

    Returns:
    0 on success or a negative error code.
    """
    msg = nlmsg_alloc()
    genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, nl80211.NL80211_CMD_GET_SCAN, 0)
    nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index)
    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_dump, results)
    _LOGGER.debug('Sending NL80211_CMD_GET_SCAN...')
    ret = nl_send_auto(sk, msg)
    if ret >= 0:
        _LOGGER.debug('Retrieving NL80211_CMD_GET_SCAN response...')
        ret = nl_recvmsgs(sk, cb)
    return ret
예제 #5
0
    def update_iface_details(self, cmd):
        # Send a command specified by CMD to the kernel and attach a callback to
        # process the returned values into our own datastructure

        self._nl_sock = nl_socket_alloc()  # Creates an `nl_sock` instance.
        # Create file descriptor and bind socket.
        ret = genl_connect(self._nl_sock)
        if ret < 0:
            reason = errmsg[abs(ret)]
            logger.error('genl_connect() returned {0} ({1})'.format(
                ret, reason))
            return {}

        # Now get the nl80211 driver ID. Handle errors here.
        # Find the nl80211 driver ID.
        driver_id = genl_ctrl_resolve(self._nl_sock, b'nl80211')
        if driver_id < 0:
            reason = errmsg[abs(driver_id)]
            logger.error('genl_ctrl_resolve() returned {0} ({1})'.format(
                driver_id, reason))
            return {}

        # Setup the Generic Netlink message.
        msg = nlmsg_alloc()  # Allocate a message.
        if self.if_idx == None:
            # Ask kernel to send info for all wireless interfaces.
            genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP,
                        nl80211.NL80211_CMD_GET_INTERFACE, 0)
        else:
            genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, cmd, 0)
            # This is the interface we care about.
            nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, self.if_idx)
            #nla_put_u32(msg, nl80211.NL80211_ATTR_MAC, 2199023255552)

        # Add the callback function to the self._nl_sock.
        nl_socket_modify_cb(self._nl_sock, NL_CB_VALID, NL_CB_CUSTOM,
                            self._iface_callback, False)

        # Now send the message to the kernel, and get its response,
        # automatically calling the callback.
        ret = nl_send_auto(self._nl_sock, msg)
        if ret < 0:
            reason = errmsg[abs(ret)]
            logger.error('nl_send_auto() returned {0} ({1})'.format(
                ret, reason))
            return {}
        logger.debug('Sent {0} bytes to the kernel.'.format(ret))
        # Blocks until the kernel replies. Usually it's instant.
        ret = nl_recvmsgs_default(self._nl_sock)
        if ret < 0:
            reason = errmsg[abs(ret)]
            logger.error('nl_recvmsgs_default() returned {0} ({1})'.format(
                ret, reason))
            return {}
예제 #6
0
def main():
    """Main function called upon script execution."""
    # First get the wireless interface index.
    if OPTIONS['<interface>']:
        pack = struct.pack('16sI', OPTIONS['<interface>'].encode('ascii'), 0)
        sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack))
        except OSError:
            return error('Wireless interface {0} does not exist.'.format(OPTIONS['<interface>']))
        finally:
            sk.close()
        if_index = int(info[1])
    else:
        if_index = -1

    # Then open a socket to the kernel. Same one used for sending and receiving.
    sk = nl_socket_alloc()  # Creates an `nl_sock` instance.
    ret = genl_connect(sk)  # Create file descriptor and bind socket.
    if ret < 0:
        reason = errmsg[abs(ret)]
        return error('genl_connect() returned {0} ({1})'.format(ret, reason))

    # Now get the nl80211 driver ID. Handle errors here.
    driver_id = genl_ctrl_resolve(sk, b'nl80211')  # Find the nl80211 driver ID.
    if driver_id < 0:
        reason = errmsg[abs(driver_id)]
        return error('genl_ctrl_resolve() returned {0} ({1})'.format(driver_id, reason))

    # Setup the Generic Netlink message.
    msg = nlmsg_alloc()  # Allocate a message.
    if OPTIONS['<interface>']:
        genlmsg_put(msg, 0, 0, driver_id, 0, 0, nl80211.NL80211_CMD_GET_INTERFACE, 0)  # Tell kernel: send iface info.
        nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index)  # This is the interface we care about.
    else:
        # Ask kernel to send info for all wireless interfaces.
        genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, nl80211.NL80211_CMD_GET_INTERFACE, 0)

    # Add the callback function to the nl_sock.
    has_printed = list()
    nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, has_printed)

    # Now send the message to the kernel, and get its response, automatically calling the callback.
    ret = nl_send_auto(sk, msg)
    if ret < 0:
        reason = errmsg[abs(ret)]
        return error('nl_send_auto() returned {0} ({1})'.format(ret, reason))
    print('Sent {0} bytes to the kernel.'.format(ret))
    ret = nl_recvmsgs_default(sk)  # Blocks until the kernel replies. Usually it's instant.
    if ret < 0:
        reason = errmsg[abs(ret)]
        return error('nl_recvmsgs_default() returned {0} ({1})'.format(ret, reason))
예제 #7
0
def read_function():
    # Initialize the message sent by netlink socket
    msg = nlmsg_alloc()
    # Use command CMD_GET_STATION to retreive the connected stations attributes
    # With Hostapd, the connected stations are the clients
    # See https://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git/tree/include/uapi/linux/nl80211.h?id=HEAD#n222
    genlmsg_put(msg, 0, 0, DRIVER_ID, 0, NLM_F_DUMP,
                nl80211.NL80211_CMD_GET_STATION, 0)
    # Set the network interface of the device we are working with
    # See https://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git/tree/include/uapi/linux/nl80211.h?id=HEAD#n1032
    nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, INTERFACEINDEX)
    # Finalize and transmit message
    nl_send_auto(SOCKET, msg)
    # This list will contain the results of the kernel
    results = []
    # Bind the callbacks methods for events NL_CB_VALID and NL_CB_FINISH
    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,
                             getStationInfo_callback, results)
    libnl.handlers.nl_cb_set(cb, libnl.handlers.NL_CB_FINISH,
                             libnl.handlers.NL_CB_CUSTOM, finish_callback,
                             results)
    # Receive messages from Kernel
    nl_recvmsgs(SOCKET, cb)
    while len(results) == 0:
        continue
    # Configure the collectd data sending object
    VALUES.plugin = "hostapd"
    VALUES.plugin_instance = INTERFACE
    VALUES.type = 'gauge'
    VALUES.type_instance = 'stations-count'
    # If no clients are connected, just send 0 to the metrics storage backend,
    # otherwise, send the count and the attributes of clients
    if results[-1] == -1:
        VALUES.dispatch(values=[0])
    else:
        VALUES.dispatch(values=[len(results)])
        # Browse the stations returned by the kernel
        for station in results:
            # If we shouldn't send data for every clients, we check the MAC address
            if len(CLIENTS) > 0:
                if station.mac_addr in CLIENTS:
                    send_station_stats(station)
            # If not, just send the data
            else:
                send_station_stats(station)

    # Clean a few values to avoid memory leak
    del (msg)
    del (cb)
    del (results)
예제 #8
0
def ncsi_get_info(ifindex, package):

    # Open socket to kernel
    sk = nl_socket_alloc()
    ret = genl_connect(sk)
    if ret < 0:
        print("Failed to open socket")
        return -1

    # Find NCSI
    driver_id = genl_ctrl_resolve(sk, b'NCSI')
    if driver_id < 0:
        print("Could not resolve NCSI")
        return -1

    # Setup up a Generic Netlink message
    msg = nlmsg_alloc()
    if package is None:
        ret = genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP,
                          NCSI_CMD_PKG_INFO, 0)
    else:
        ret = genlmsg_put(msg, 0, 0, driver_id, 0, 0, NCSI_CMD_PKG_INFO, 0)
        nla_put_u32(msg, NCSI_ATTR_PACKAGE_ID, int(package))

    if ret < 0:
        reason = errmsg[abs(ret)]
        print("genlmsg_put returned {}, {}".format(ret, reason))
        return -1

    nla_put_u32(msg, NCSI_ATTR_IFINDEX, ifindex)

    # Add a callback function to the socket
    nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, info_callback, None)

    ret = nl_send_auto(sk, msg)
    if ret < 0:
        print("Failed to send message: {}".format(ret))
        return ret
    ret = nl_recvmsgs_default(sk)  # blocks
    if ret < 0:
        reason = errmsg[abs(ret)]
        print("recvmsg returned {}, {}".format(ret, reason))
예제 #9
0
def test_two_attrs():
    """Test with two attributes."""
    msg = nlmsg_alloc()
    assert 0 == nla_put_u32(msg, 4, 8)
    nlh = nlmsg_hdr(msg)
    assert 24 == nlh.nlmsg_len
    assert 0 == nlh.nlmsg_type
    assert 0 == nlh.nlmsg_flags
    assert 0 == nlh.nlmsg_seq
    assert 0 == nlh.nlmsg_pid
    assert 0 == nla_put_u64(msg, 5, 17)
    assert 36 == nlh.nlmsg_len

    sk = nl_socket_alloc()
    nl_connect(sk, NETLINK_ROUTE)
    sk.socket_instance.close()
    sk.s_local.nl_pid = 0
    sk.s_seq_next = 0
    nl_complete_msg(sk, msg)
    assert 0 == nlh.nlmsg_seq
    nlh.nlmsg_pid = 0  # sk.s_local.nl_pid is read-only in Python.

    expected = b'JAAAAAAABQAAAAAAAAAAAAgABAAIAAAADAAFABEAAAAAAAAA'
    assert expected == base64.b64encode(buffer(nlh.bytearray[:nlh.nlmsg_len]))
예제 #10
0
def test_two_attrs():
    """Test with two attributes."""
    msg = nlmsg_alloc()
    assert 0 == nla_put_u32(msg, 4, 8)
    nlh = nlmsg_hdr(msg)
    assert 24 == nlh.nlmsg_len
    assert 0 == nlh.nlmsg_type
    assert 0 == nlh.nlmsg_flags
    assert 0 == nlh.nlmsg_seq
    assert 0 == nlh.nlmsg_pid
    assert 0 == nla_put_u64(msg, 5, 17)
    assert 36 == nlh.nlmsg_len

    sk = nl_socket_alloc()
    nl_connect(sk, NETLINK_ROUTE)
    sk.socket_instance.close()
    sk.s_local.nl_pid = 0
    sk.s_seq_next = 0
    nl_complete_msg(sk, msg)
    assert 0 == nlh.nlmsg_seq
    nlh.nlmsg_pid = 0  # sk.s_local.nl_pid is read-only in Python.

    expected = b'JAAAAAAABQAAAAAAAAAAAAgABAAIAAAADAAFABEAAAAAAAAA'
    assert expected == base64.b64encode(buffer(nlh.bytearray[:nlh.nlmsg_len]))
def main():
    """Main function called upon script execution."""
    # First get the wireless interface index.
    if OPTIONS['<interface>']:
        pack = struct.pack('16sI', OPTIONS['<interface>'].encode('ascii'), 0)
        sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933,
                                                     pack))
        except OSError:
            return error('Wireless interface {0} does not exist.'.format(
                OPTIONS['<interface>']))
        finally:
            sk.close()
        if_index = int(info[1])
    else:
        if_index = -1

    # Then open a socket to the kernel. Same one used for sending and receiving.
    sk = nl_socket_alloc()  # Creates an `nl_sock` instance.
    ret = genl_connect(sk)  # Create file descriptor and bind socket.
    if ret < 0:
        reason = errmsg[abs(ret)]
        return error('genl_connect() returned {0} ({1})'.format(ret, reason))

    # Now get the nl80211 driver ID. Handle errors here.
    driver_id = genl_ctrl_resolve(sk,
                                  b'nl80211')  # Find the nl80211 driver ID.
    if driver_id < 0:
        reason = errmsg[abs(driver_id)]
        return error('genl_ctrl_resolve() returned {0} ({1})'.format(
            driver_id, reason))

    # Setup the Generic Netlink message.
    msg = nlmsg_alloc()  # Allocate a message.
    if OPTIONS['<interface>']:
        genlmsg_put(msg, 0, 0, driver_id, 0, 0,
                    nl80211.NL80211_CMD_GET_INTERFACE,
                    0)  # Tell kernel: send iface info.
        nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX,
                    if_index)  # This is the interface we care about.
    else:
        # Ask kernel to send info for all wireless interfaces.
        genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP,
                    nl80211.NL80211_CMD_GET_INTERFACE, 0)

    # Add the callback function to the nl_sock.
    has_printed = list()
    nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, has_printed)

    # Now send the message to the kernel, and get its response, automatically calling the callback.
    ret = nl_send_auto(sk, msg)
    if ret < 0:
        reason = errmsg[abs(ret)]
        return error('nl_send_auto() returned {0} ({1})'.format(ret, reason))
    print('Sent {0} bytes to the kernel.'.format(ret))
    ret = nl_recvmsgs_default(
        sk)  # Blocks until the kernel replies. Usually it's instant.
    if ret < 0:
        reason = errmsg[abs(ret)]
        return error('nl_recvmsgs_default() returned {0} ({1})'.format(
            ret, reason))
예제 #12
0
    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)
예제 #13
0
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.
예제 #14
0
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.
def channel_hopper():
    global count

    # Get the index of the interface
    pack = struct.pack('16sI', interface, 0)
    sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack))
    except (OSError, IOError):
        error('wireless interface {0} does not exist.'.format(interface))
        return -1
    finally:
        sk.close()

    if_index = int(info[1])

    # Open a socket to the kernel
    sk = nl_socket_alloc()
    ret = genl_connect(sk)
    if ret < 0:
        reason = errmsg[abs(ret)]
        error('genl_connect() failed: {0} ({1})'.format(ret, reason))
        return -1

    # Now get the nl80211 driver ID
    driver_id = genl_ctrl_resolve(sk, b'nl80211')
    if driver_id < 0:
        reason = errmsg[abs(driver_id)]
        error('genl_ctrl_resolve() failed: {0} ({1})'.format(
            driver_id, reason))
        return -1

    # Iterate over channels using the corresponding dwell time
    supported_channels = [x for x in channels if x not in unsupported_channels]
    for ch in supported_channels:

        if not sniffing:
            break

        # Set new channel
        msg = nlmsg_alloc()
        genlmsg_put(msg, 0, 0, driver_id, 0, 0,
                    nl80211.NL80211_CMD_SET_CHANNEL, 0)
        nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index)
        nla_put_u32(msg, nl80211.NL80211_ATTR_WIPHY_FREQ,
                    channel_to_frequency(ch))
        nla_put_u32(msg, nl80211.NL80211_ATTR_WIPHY_CHANNEL_TYPE,
                    nl80211.NL80211_CHAN_WIDTH_20)

        if nl_send_auto(sk, msg) < 0:
            unsupported_channels.add(ch)
            time.sleep(0.02)
        else:
            count = 0
            time.sleep(dwelltimes[ch])
            if count == 0:
                dwelltimes[ch] = mindwelltime
            else:
                dwelltimes[ch] = maxdwelltime

    return 0