Example #1
0
def genlmsg_put(msg, port, seq, family, hdrlen, flags, cmd, version):
    """Add Generic Netlink headers to Netlink message.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L348

    Calls nlmsg_put() on the specified message object to reserve space for the Netlink header, the Generic Netlink
    header, and a user header of specified length. Fills out the header fields with the specified parameters.

    Positional arguments:
    msg -- Netlink message object (nl_msg class instance).
    port -- Netlink port or NL_AUTO_PORT (c_uint32).
    seq -- sequence number of message or NL_AUTO_SEQ (c_uint32).
    family -- numeric family identifier (integer).
    hdrlen -- length of user header (integer).
    flags -- additional Netlink message flags (integer).
    cmd -- numeric command identifier (c_uint8).
    version -- interface version (c_uint8).

    Returns:
    bytearray starting at user header or None if an error occurred.
    """
    hdr = genlmsghdr(cmd=cmd, version=version)
    nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags)
    if nlh is None:
        return None
    nlmsg_data(nlh)[:hdr.SIZEOF] = hdr.bytearray[:hdr.SIZEOF]
    _LOGGER.debug('msg 0x%x: Added generic netlink header cmd=%d version=%d',
                  id(msg), cmd, version)
    return bytearray_ptr(nlmsg_data(nlh), GENL_HDRLEN)
Example #2
0
File: genl.py Project: 0x90/libnl
def genlmsg_put(msg, port, seq, family, hdrlen, flags, cmd, version):
    """Add Generic Netlink headers to Netlink message.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L348

    Calls nlmsg_put() on the specified message object to reserve space for the Netlink header, the Generic Netlink
    header, and a user header of specified length. Fills out the header fields with the specified parameters.

    Positional arguments:
    msg -- Netlink message object (nl_msg class instance).
    port -- Netlink port or NL_AUTO_PORT (c_uint32).
    seq -- sequence number of message or NL_AUTO_SEQ (c_uint32).
    family -- numeric family identifier (integer).
    hdrlen -- length of user header (integer).
    flags -- additional Netlink message flags (integer).
    cmd -- numeric command identifier (c_uint8).
    version -- interface version (c_uint8).

    Returns:
    bytearray starting at user header or None if an error occurred.
    """
    hdr = genlmsghdr(cmd=cmd, version=version)
    nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags)
    if nlh is None:
        return None
    nlmsg_data(nlh)[:hdr.SIZEOF] = hdr.bytearray[:hdr.SIZEOF]
    _LOGGER.debug('msg 0x%x: Added generic netlink header cmd=%d version=%d', id(msg), cmd, version)
    return bytearray_ptr(nlmsg_data(nlh), GENL_HDRLEN)
Example #3
0
def genlmsg_parse(nlh, hdrlen, tb, maxtype, policy):
    """Parse Generic Netlink message including attributes.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L191

    Verifies the validity of the Netlink and Generic Netlink headers using genlmsg_valid_hdr() and calls nla_parse() on
    the message payload to parse eventual attributes.

    Positional arguments:
    nlh -- Netlink message header (nlmsghdr class instance).
    hdrlen -- length of user header (integer).
    tb -- empty dict, to be updated with nlattr class instances to store parsed attributes.
    maxtype -- maximum attribute id expected (integer).
    policy -- dictionary of nla_policy class instances as values, with nla types as keys.

    Returns:
    0 on success or a negative error code.
    """
    if not genlmsg_valid_hdr(nlh, hdrlen):
        return -NLE_MSG_TOOSHORT

    ghdr = genlmsghdr(nlmsg_data(nlh))
    return int(
        nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen),
                  genlmsg_attrlen(ghdr, hdrlen), policy))
Example #4
0
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
Example #5
0
def callback(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_OK. It tells libnl to proceed with processing the next kernel message.
    """
    # First convert `msg` into something more manageable.
    nlh = nlmsg_hdr(msg)
    iface = ifinfomsg(nlmsg_data(nlh))
    hdr = IFLA_RTA(iface)
    remaining = ctypes.c_int(nlh.nlmsg_len - NLMSG_LENGTH(iface.SIZEOF))

    # Now iterate through each rtattr stored in `iface`.
    while RTA_OK(hdr, remaining):
        # Each rtattr (which is what hdr is) instance is only one type. Looping through all of them until we run into
        # the ones we care about.
        if hdr.rta_type == IFLA_IFNAME:
            print('Found network interface {0}: {1}'.format(
                iface.ifi_index,
                get_string(RTA_DATA(hdr)).decode('ascii')))
        hdr = RTA_NEXT(hdr, remaining)
    return NL_OK
Example #6
0
    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
Example #7
0
File: genl.py Project: 0x90/libnl
def genlmsg_valid_hdr(nlh, hdrlen):
    """Validate Generic Netlink message headers.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L117

    Verifies the integrity of the Netlink and Generic Netlink headers by enforcing the following requirements:
    - Valid Netlink message header (`nlmsg_valid_hdr()`)
    - Presence of a complete Generic Netlink header
    - At least `hdrlen` bytes of payload included after the generic Netlink header.

    Positional arguments:
    nlh -- Netlink message header (nlmsghdr class instance).
    hdrlen -- length of user header (integer).

    Returns:
    True if the headers are valid or False if not.
    """
    if not nlmsg_valid_hdr(nlh, GENL_HDRLEN):
        return False

    ghdr = genlmsghdr(nlmsg_data(nlh))
    if genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen):
        return False

    return True
Example #8
0
def genlmsg_valid_hdr(nlh, hdrlen):
    """Validate Generic Netlink message headers.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L117

    Verifies the integrity of the Netlink and Generic Netlink headers by enforcing the following requirements:
    - Valid Netlink message header (`nlmsg_valid_hdr()`)
    - Presence of a complete Generic Netlink header
    - At least `hdrlen` bytes of payload included after the generic Netlink header.

    Positional arguments:
    nlh -- Netlink message header (nlmsghdr class instance).
    hdrlen -- length of user header (integer).

    Returns:
    True if the headers are valid or False if not.
    """
    if not nlmsg_valid_hdr(nlh, GENL_HDRLEN):
        return False

    ghdr = genlmsghdr(nlmsg_data(nlh))
    if genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen):
        return False

    return True
Example #9
0
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
Example #10
0
    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
Example #12
0
    def callback(msg, arg):
        nlh = nlmsg_hdr(msg)
        iface = ifinfomsg(nlmsg_data(nlh))
        hdr = IFLA_RTA(iface)
        remaining = c_int(nlh.nlmsg_len - NLMSG_LENGTH(iface.SIZEOF))

        while RTA_OK(hdr, remaining):
            if hdr.rta_type == IFLA_IFNAME:
                arg[int(iface.ifi_index)] = str(get_string(RTA_DATA(hdr)).decode('ascii'))
            hdr = RTA_NEXT(hdr, remaining)
        return NL_OK
Example #13
0
File: genl.py Project: 0x90/libnl
def genlmsg_hdr(nlh):
    """Return reference to Generic Netlink header.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L210

    Positional arguments:
    nlh -- Netlink message header (nlmsghdr class instance).

    Returns:
    Reference to Generic Netlink message header.
    """
    return nlmsg_data(nlh)
Example #14
0
def genlmsg_hdr(nlh):
    """Return reference to Generic Netlink header.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L210

    Positional arguments:
    nlh -- Netlink message header (nlmsghdr class instance).

    Returns:
    Reference to Generic Netlink message header.
    """
    return nlmsg_data(nlh)
Example #15
0
def callback_trigger(msg, arg):
    """Called when the kernel is done scanning. Only signals if it was successful or if it failed. No other data.

    Positional arguments:
    msg -- nl_msg class instance containing the data sent by the kernel.
    arg -- mutable integer (ctypes.c_int()) to update with results.

    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.
    """
    gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg)))
    if gnlh.cmd == nl80211.NL80211_CMD_SCAN_ABORTED:
        arg.value = 1  # The scan was aborted for some reason.
    elif gnlh.cmd == nl80211.NL80211_CMD_NEW_SCAN_RESULTS:
        arg.value = 0  # The scan completed successfully. `callback_dump` will collect the results later.
    return libnl.handlers.NL_SKIP
Example #16
0
def callback_trigger(msg, arg):
    """Called when the kernel is done scanning. Only signals if it was successful or if it failed. No other data.

    Positional arguments:
    msg -- nl_msg class instance containing the data sent by the kernel.
    arg -- mutable integer (ctypes.c_int()) to update with results.

    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.
    """
    gnlh = genlmsghdr(nlmsg_data(nlmsg_hdr(msg)))
    if gnlh.cmd == nl80211.NL80211_CMD_SCAN_ABORTED:
        arg.value = 1  # The scan was aborted for some reason.
    elif gnlh.cmd == nl80211.NL80211_CMD_NEW_SCAN_RESULTS:
        arg.value = 0  # The scan completed successfully. `callback_dump` will collect the results later.
    return libnl.handlers.NL_SKIP
Example #17
0
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
Example #18
0
File: genl.py Project: 0x90/libnl
def genlmsg_parse(nlh, hdrlen, tb, maxtype, policy):
    """Parse Generic Netlink message including attributes.

    https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L191

    Verifies the validity of the Netlink and Generic Netlink headers using genlmsg_valid_hdr() and calls nla_parse() on
    the message payload to parse eventual attributes.

    Positional arguments:
    nlh -- Netlink message header (nlmsghdr class instance).
    hdrlen -- length of user header (integer).
    tb -- empty dict, to be updated with nlattr class instances to store parsed attributes.
    maxtype -- maximum attribute id expected (integer).
    policy -- dictionary of nla_policy class instances as values, with nla types as keys.

    Returns:
    0 on success or a negative error code.
    """
    if not genlmsg_valid_hdr(nlh, hdrlen):
        return -NLE_MSG_TOOSHORT

    ghdr = genlmsghdr(nlmsg_data(nlh))
    return int(nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen), genlmsg_attrlen(ghdr, hdrlen), policy))
def callback(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_OK. It tells libnl to proceed with processing the next kernel message.
    """
    # First convert `msg` into something more manageable.
    nlh = nlmsg_hdr(msg)
    iface = ifinfomsg(nlmsg_data(nlh))
    hdr = IFLA_RTA(iface)
    remaining = ctypes.c_int(nlh.nlmsg_len - NLMSG_LENGTH(iface.SIZEOF))

    # Now iterate through each rtattr stored in `iface`.
    while RTA_OK(hdr, remaining):
        # Each rtattr (which is what hdr is) instance is only one type. Looping through all of them until we run into
        # the ones we care about.
        if hdr.rta_type == IFLA_IFNAME:
            print('Found network interface {0}: {1}'.format(iface.ifi_index, get_string(RTA_DATA(hdr)).decode('ascii')))
        hdr = RTA_NEXT(hdr, remaining)
    return NL_OK
Example #20
0
def recvmsgs(sk, cb):
    """https://github.com/thom311/libnl/blob/libnl3_2_25/lib/nl.c#L775.

    This is where callbacks are called.

    Positional arguments:
    sk -- Netlink socket (nl_sock class instance).
    cb -- callbacks (nl_cb class instance).

    Returns:
    Number of bytes received or a negative error code.
    """
    multipart = 0
    interrupted = 0
    nrecv = 0
    buf = bytearray()

    # nla is passed on to not only to nl_recv() but may also be passed to a function pointer provided by the caller
    # which may or may not initialize the variable. Thomas Graf.
    nla = sockaddr_nl()
    creds = ucred()

    while True:  # This is the `goto continue_reading` implementation.
        _LOGGER.debug('Attempting to read from 0x%x', id(sk))
        n = c_int(
            cb.cb_recv_ow(sk, nla, buf, creds) if cb.
            cb_recv_ow else nl_recv(sk, nla, buf, creds))
        if n.value <= 0:
            return n.value

        _LOGGER.debug('recvmsgs(0x%x): Read %d bytes', id(sk), n.value)

        hdr = nlmsghdr(bytearray_ptr(buf))
        while nlmsg_ok(hdr, n):
            _LOGGER.debug('recvmsgs(0x%x): Processing valid message...',
                          id(sk))
            msg = nlmsg_convert(hdr)
            nlmsg_set_proto(msg, sk.s_proto)
            nlmsg_set_src(msg, nla)
            if creds:
                raise NotImplementedError  # nlmsg_set_creds(msg, creds)
            nrecv += 1

            # Raw callback is the first, it gives the most control to the user and he can do his very own parsing.
            if cb.cb_set[NL_CB_MSG_IN]:
                err = nl_cb_call(cb, NL_CB_MSG_IN,
                                 msg)  # NL_CB_CALL(cb, NL_CB_MSG_IN, msg)
                if err == NL_OK:
                    pass
                elif err == NL_SKIP:
                    hdr = nlmsg_next(hdr, n)
                    continue
                elif err == NL_STOP:
                    return -NLE_DUMP_INTR if interrupted else nrecv
                else:
                    return -NLE_DUMP_INTR if interrupted else (err or nrecv)

            if cb.cb_set[NL_CB_SEQ_CHECK]:
                # Sequence number checking. The check may be done by the user, otherwise a very simple check is applied
                # enforcing strict ordering.
                err = nl_cb_call(cb, NL_CB_SEQ_CHECK,
                                 msg)  # NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg)
                if err == NL_OK:
                    pass
                elif err == NL_SKIP:
                    hdr = nlmsg_next(hdr, n)
                    continue
                elif err == NL_STOP:
                    return -NLE_DUMP_INTR if interrupted else nrecv
                else:
                    return -NLE_DUMP_INTR if interrupted else (err or nrecv)
            elif not sk.s_flags & NL_NO_AUTO_ACK:
                # Only do sequence checking if auto-ack mode is enabled.
                if hdr.nlmsg_seq != sk.s_seq_expect:
                    if cb.cb_set[NL_CB_INVALID]:
                        err = nl_cb_call(
                            cb, NL_CB_INVALID,
                            msg)  # NL_CB_CALL(cb, NL_CB_INVALID, msg)
                        if err == NL_OK:
                            pass
                        elif err == NL_SKIP:
                            hdr = nlmsg_next(hdr, n)
                            continue
                        elif err == NL_STOP:
                            return -NLE_DUMP_INTR if interrupted else nrecv
                        else:
                            return -NLE_DUMP_INTR if interrupted else (
                                err or nrecv)
                    else:
                        return -NLE_SEQ_MISMATCH

            if hdr.nlmsg_type in (NLMSG_DONE, NLMSG_ERROR, NLMSG_NOOP,
                                  NLMSG_OVERRUN):
                # We can't check for !NLM_F_MULTI since some Netlink users in the kernel are broken.
                sk.s_seq_expect += 1
                _LOGGER.debug(
                    'recvmsgs(0x%x): Increased expected sequence number to %d',
                    id(sk), sk.s_seq_expect)

            if hdr.nlmsg_flags & NLM_F_MULTI:
                multipart = 1

            if hdr.nlmsg_flags & NLM_F_DUMP_INTR:
                if cb.cb_set[NL_CB_DUMP_INTR]:
                    err = nl_cb_call(
                        cb, NL_CB_DUMP_INTR,
                        msg)  # NL_CB_CALL(cb, NL_CB_DUMP_INTR, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)
                else:
                    # We have to continue reading to clear all messages until a NLMSG_DONE is received and report the
                    # inconsistency.
                    interrupted = 1

            if hdr.nlmsg_flags & NLM_F_ACK:
                # Other side wishes to see an ack for this message.
                if cb.cb_set[NL_CB_SEND_ACK]:
                    err = nl_cb_call(
                        cb, NL_CB_SEND_ACK,
                        msg)  # NL_CB_CALL(cb, NL_CB_SEND_ACK, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)

            if hdr.nlmsg_type == NLMSG_DONE:
                # Messages terminates a multipart message, this is usually the end of a message and therefore we slip
                # out of the loop by default. the user may overrule this action by skipping this packet.
                multipart = 0
                if cb.cb_set[NL_CB_FINISH]:
                    err = nl_cb_call(cb, NL_CB_FINISH,
                                     msg)  # NL_CB_CALL(cb, NL_CB_FINISH, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)
            elif hdr.nlmsg_type == NLMSG_NOOP:
                # Message to be ignored, the default action is to skip this message if no callback is specified. The
                # user may overrule this action by returning NL_PROCEED.
                if cb.cb_set[NL_CB_SKIPPED]:
                    err = nl_cb_call(cb, NL_CB_SKIPPED,
                                     msg)  # NL_CB_CALL(cb, NL_CB_SKIPPED, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)
                else:
                    hdr = nlmsg_next(hdr, n)
                    continue
            elif hdr.nlmsg_type == NLMSG_OVERRUN:
                # Data got lost, report back to user. The default action is to quit parsing. The user may overrule this
                # action by retuning NL_SKIP or NL_PROCEED (dangerous).
                if cb.cb_set[NL_CB_OVERRUN]:
                    err = nl_cb_call(cb, NL_CB_OVERRUN,
                                     msg)  # NL_CB_CALL(cb, NL_CB_OVERRUN, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)
                else:
                    return -NLE_DUMP_INTR if interrupted else -NLE_MSG_OVERFLOW
            elif hdr.nlmsg_type == NLMSG_ERROR:
                # Message carries a nlmsgerr.
                e = nlmsgerr(nlmsg_data(hdr))
                if hdr.nlmsg_len < nlmsg_size(e.SIZEOF):
                    # Truncated error message, the default action is to stop parsing. The user may overrule this action
                    # by returning NL_SKIP or NL_PROCEED (dangerous).
                    if cb.cb_set[NL_CB_INVALID]:
                        err = nl_cb_call(
                            cb, NL_CB_INVALID,
                            msg)  # NL_CB_CALL(cb, NL_CB_INVALID, msg)
                        if err == NL_OK:
                            pass
                        elif err == NL_SKIP:
                            hdr = nlmsg_next(hdr, n)
                            continue
                        elif err == NL_STOP:
                            return -NLE_DUMP_INTR if interrupted else nrecv
                        else:
                            return -NLE_DUMP_INTR if interrupted else (
                                err or nrecv)
                    else:
                        return -NLE_DUMP_INTR if interrupted else -NLE_MSG_TRUNC
                elif e.error:
                    # Error message reported back from kernel.
                    if cb.cb_err:
                        err = cb.cb_err(nla, e, cb.cb_err_arg)
                        if err < 0:
                            return -NLE_DUMP_INTR if interrupted else err
                        elif err == NL_SKIP:
                            hdr = nlmsg_next(hdr, n)
                            continue
                        elif err == NL_STOP:
                            return -NLE_DUMP_INTR if interrupted else -nl_syserr2nlerr(
                                e.error)
                    else:
                        return -NLE_DUMP_INTR if interrupted else -nl_syserr2nlerr(
                            e.error)
                elif cb.cb_set[NL_CB_ACK]:
                    err = nl_cb_call(cb, NL_CB_ACK,
                                     msg)  # NL_CB_CALL(cb, NL_CB_ACK, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)
            else:
                # Valid message (not checking for MULTIPART bit to get along with broken kernels. NL_SKIP has no effect
                # on this.
                if cb.cb_set[NL_CB_VALID]:
                    err = nl_cb_call(cb, NL_CB_VALID,
                                     msg)  # NL_CB_CALL(cb, NL_CB_VALID, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err
                                                                   or nrecv)

            hdr = nlmsg_next(hdr, n)

        del buf[:]
        creds = None

        if multipart:
            # Multipart message not yet complete, continue reading.
            continue

        err = 0
        if interrupted:
            return -NLE_DUMP_INTR
        if not err:
            err = nrecv
        return err
    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
Example #22
0
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
Example #23
0
File: nl.py Project: 0x90/libnl
def recvmsgs(sk, cb):
    """https://github.com/thom311/libnl/blob/libnl3_2_25/lib/nl.c#L775.

    This is where callbacks are called.

    Positional arguments:
    sk -- Netlink socket (nl_sock class instance).
    cb -- callbacks (nl_cb class instance).

    Returns:
    Number of bytes received or a negative error code.
    """
    multipart = 0
    interrupted = 0
    nrecv = 0
    buf = bytearray()

    # nla is passed on to not only to nl_recv() but may also be passed to a function pointer provided by the caller
    # which may or may not initialize the variable. Thomas Graf.
    nla = sockaddr_nl()
    creds = ucred()

    while True:  # This is the `goto continue_reading` implementation.
        _LOGGER.debug('Attempting to read from 0x%x', id(sk))
        n = c_int(cb.cb_recv_ow(sk, nla, buf, creds) if cb.cb_recv_ow else nl_recv(sk, nla, buf, creds))
        if n.value <= 0:
            return n.value

        _LOGGER.debug('recvmsgs(0x%x): Read %d bytes', id(sk), n.value)

        hdr = nlmsghdr(bytearray_ptr(buf))
        while nlmsg_ok(hdr, n):
            _LOGGER.debug('recvmsgs(0x%x): Processing valid message...', id(sk))
            msg = nlmsg_convert(hdr)
            nlmsg_set_proto(msg, sk.s_proto)
            nlmsg_set_src(msg, nla)
            if creds:
                raise NotImplementedError  # nlmsg_set_creds(msg, creds)
            nrecv += 1

            # Raw callback is the first, it gives the most control to the user and he can do his very own parsing.
            if cb.cb_set[NL_CB_MSG_IN]:
                err = nl_cb_call(cb, NL_CB_MSG_IN, msg)  # NL_CB_CALL(cb, NL_CB_MSG_IN, msg)
                if err == NL_OK:
                    pass
                elif err == NL_SKIP:
                    hdr = nlmsg_next(hdr, n)
                    continue
                elif err == NL_STOP:
                    return -NLE_DUMP_INTR if interrupted else nrecv
                else:
                    return -NLE_DUMP_INTR if interrupted else (err or nrecv)

            if cb.cb_set[NL_CB_SEQ_CHECK]:
                # Sequence number checking. The check may be done by the user, otherwise a very simple check is applied
                # enforcing strict ordering.
                err = nl_cb_call(cb, NL_CB_SEQ_CHECK, msg)  # NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg)
                if err == NL_OK:
                    pass
                elif err == NL_SKIP:
                    hdr = nlmsg_next(hdr, n)
                    continue
                elif err == NL_STOP:
                    return -NLE_DUMP_INTR if interrupted else nrecv
                else:
                    return -NLE_DUMP_INTR if interrupted else (err or nrecv)
            elif not sk.s_flags & NL_NO_AUTO_ACK:
                # Only do sequence checking if auto-ack mode is enabled.
                if hdr.nlmsg_seq != sk.s_seq_expect:
                    if cb.cb_set[NL_CB_INVALID]:
                        err = nl_cb_call(cb, NL_CB_INVALID, msg)  # NL_CB_CALL(cb, NL_CB_INVALID, msg)
                        if err == NL_OK:
                            pass
                        elif err == NL_SKIP:
                            hdr = nlmsg_next(hdr, n)
                            continue
                        elif err == NL_STOP:
                            return -NLE_DUMP_INTR if interrupted else nrecv
                        else:
                            return -NLE_DUMP_INTR if interrupted else (err or nrecv)
                    else:
                        return -NLE_SEQ_MISMATCH

            if hdr.nlmsg_type in (NLMSG_DONE, NLMSG_ERROR, NLMSG_NOOP, NLMSG_OVERRUN):
                # We can't check for !NLM_F_MULTI since some Netlink users in the kernel are broken.
                sk.s_seq_expect += 1
                _LOGGER.debug('recvmsgs(0x%x): Increased expected sequence number to %d', id(sk), sk.s_seq_expect)

            if hdr.nlmsg_flags & NLM_F_MULTI:
                multipart = 1

            if hdr.nlmsg_flags & NLM_F_DUMP_INTR:
                if cb.cb_set[NL_CB_DUMP_INTR]:
                    err = nl_cb_call(cb, NL_CB_DUMP_INTR, msg)  # NL_CB_CALL(cb, NL_CB_DUMP_INTR, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)
                else:
                    # We have to continue reading to clear all messages until a NLMSG_DONE is received and report the
                    # inconsistency.
                    interrupted = 1

            if hdr.nlmsg_flags & NLM_F_ACK:
                # Other side wishes to see an ack for this message.
                if cb.cb_set[NL_CB_SEND_ACK]:
                    err = nl_cb_call(cb, NL_CB_SEND_ACK, msg)  # NL_CB_CALL(cb, NL_CB_SEND_ACK, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)

            if hdr.nlmsg_type == NLMSG_DONE:
                # Messages terminates a multipart message, this is usually the end of a message and therefore we slip
                # out of the loop by default. the user may overrule this action by skipping this packet.
                multipart = 0
                if cb.cb_set[NL_CB_FINISH]:
                    err = nl_cb_call(cb, NL_CB_FINISH, msg)  # NL_CB_CALL(cb, NL_CB_FINISH, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)
            elif hdr.nlmsg_type == NLMSG_NOOP:
                # Message to be ignored, the default action is to skip this message if no callback is specified. The
                # user may overrule this action by returning NL_PROCEED.
                if cb.cb_set[NL_CB_SKIPPED]:
                    err = nl_cb_call(cb, NL_CB_SKIPPED, msg)  # NL_CB_CALL(cb, NL_CB_SKIPPED, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)
                else:
                    hdr = nlmsg_next(hdr, n)
                    continue
            elif hdr.nlmsg_type == NLMSG_OVERRUN:
                # Data got lost, report back to user. The default action is to quit parsing. The user may overrule this
                # action by retuning NL_SKIP or NL_PROCEED (dangerous).
                if cb.cb_set[NL_CB_OVERRUN]:
                    err = nl_cb_call(cb, NL_CB_OVERRUN, msg)  # NL_CB_CALL(cb, NL_CB_OVERRUN, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)
                else:
                    return -NLE_DUMP_INTR if interrupted else -NLE_MSG_OVERFLOW
            elif hdr.nlmsg_type == NLMSG_ERROR:
                # Message carries a nlmsgerr.
                e = nlmsgerr(nlmsg_data(hdr))
                if hdr.nlmsg_len < nlmsg_size(e.SIZEOF):
                    # Truncated error message, the default action is to stop parsing. The user may overrule this action
                    # by returning NL_SKIP or NL_PROCEED (dangerous).
                    if cb.cb_set[NL_CB_INVALID]:
                        err = nl_cb_call(cb, NL_CB_INVALID, msg)  # NL_CB_CALL(cb, NL_CB_INVALID, msg)
                        if err == NL_OK:
                            pass
                        elif err == NL_SKIP:
                            hdr = nlmsg_next(hdr, n)
                            continue
                        elif err == NL_STOP:
                            return -NLE_DUMP_INTR if interrupted else nrecv
                        else:
                            return -NLE_DUMP_INTR if interrupted else (err or nrecv)
                    else:
                        return -NLE_DUMP_INTR if interrupted else -NLE_MSG_TRUNC
                elif e.error:
                    # Error message reported back from kernel.
                    if cb.cb_err:
                        err = cb.cb_err(nla, e, cb.cb_err_arg)
                        if err < 0:
                            return -NLE_DUMP_INTR if interrupted else err
                        elif err == NL_SKIP:
                            hdr = nlmsg_next(hdr, n)
                            continue
                        elif err == NL_STOP:
                            return -NLE_DUMP_INTR if interrupted else -nl_syserr2nlerr(e.error)
                    else:
                        return -NLE_DUMP_INTR if interrupted else -nl_syserr2nlerr(e.error)
                elif cb.cb_set[NL_CB_ACK]:
                    err = nl_cb_call(cb, NL_CB_ACK, msg)  # NL_CB_CALL(cb, NL_CB_ACK, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)
            else:
                # Valid message (not checking for MULTIPART bit to get along with broken kernels. NL_SKIP has no effect
                # on this.
                if cb.cb_set[NL_CB_VALID]:
                    err = nl_cb_call(cb, NL_CB_VALID, msg)  # NL_CB_CALL(cb, NL_CB_VALID, msg)
                    if err == NL_OK:
                        pass
                    elif err == NL_SKIP:
                        hdr = nlmsg_next(hdr, n)
                        continue
                    elif err == NL_STOP:
                        return -NLE_DUMP_INTR if interrupted else nrecv
                    else:
                        return -NLE_DUMP_INTR if interrupted else (err or nrecv)

            hdr = nlmsg_next(hdr, n)

        del buf[:]
        creds = None

        if multipart:
            # Multipart message not yet complete, continue reading.
            continue

        err = 0
        if interrupted:
            return -NLE_DUMP_INTR
        if not err:
            err = nrecv
        return err
Example #24
0
def info_callback(msg, _):

    nlh = nlmsg_hdr(msg)
    gnlh = genlmsghdr(nlmsg_data(nlh))
    tb = dict((i, None) for i in range(NCSI_ATTR_MAX + 1))

    ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsi_policy)
    if ret != 0:
        reason = errmsg[abs(ret)]
        print("genlmsg_parse returned {}, {}".format(ret, reason))
        return ret

    if not NCSI_ATTR_PACKAGE_LIST in tb:
        print('No packages!')
        return -1

    rem = c_int()
    for nla in nla_for_each_nested(tb[NCSI_ATTR_PACKAGE_LIST], rem):
        ptb = dict()
        ret = nla_parse_nested(ptb, NCSI_PKG_ATTR_MAX, nla,
                               ncsi_package_policy)
        if ret < 0:
            print('Failed to parse package nest')
            return ret
        if NCSI_PKG_ATTR_ID in ptb:
            print('package {}'.format(nla_get_u32(ptb[NCSI_PKG_ATTR_ID])))
        else:
            print('package (with no id?)')
        if NCSI_PKG_ATTR_FORCED in ptb:
            print('this package is forced')
        print('----------')

        crem = c_int()
        for cnla in nla_for_each_nested(ptb[NCSI_PKG_ATTR_CHANNEL_LIST], crem):
            ctb = dict()
            ret = nla_parse_nested(ctb, NCSI_CHANNEL_ATTR_MAX, cnla,
                                   ncsi_channel_policy)
            if ret < 0:
                print('Failed to parse channel nest')
                return ret
            if NCSI_CHANNEL_ATTR_ID in ctb:
                channel = nla_get_u32(ctb[NCSI_CHANNEL_ATTR_ID])
                if NCSI_CHANNEL_ATTR_ACTIVE in ctb:
                    print('channel {} - active!'.format(channel))
                else:
                    print('channel {}'.format(channel))
                if NCSI_CHANNEL_ATTR_FORCED in ctb:
                    print('\tthis channel is forced')
            else:
                print('channel (with no id?)')
            if NCSI_CHANNEL_ATTR_VERSION_MAJOR in ctb:
                print('\tmajor version {}'.format(
                    nla_get_u32(ctb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])))
            if NCSI_CHANNEL_ATTR_VERSION_MINOR in ctb:
                print('\tminor version {}'.format(
                    nla_get_u32(ctb[NCSI_CHANNEL_ATTR_VERSION_MINOR])))
            if NCSI_CHANNEL_ATTR_VERSION_STR in ctb:
                print('\tversion string {}'.format(
                    nla_get_string(ctb[NCSI_CHANNEL_ATTR_VERSION_STR])))
            if NCSI_CHANNEL_ATTR_LINK_STATE in ctb:
                print('\tlink state {}'.format(
                    nla_get_u32(ctb[NCSI_CHANNEL_ATTR_LINK_STATE])))
            if NCSI_CHANNEL_ATTR_VLAN_LIST in ctb:
                print('\tactive vlan ids:')
                rrem = c_int()
                vids = ctb[NCSI_CHANNEL_ATTR_VLAN_LIST]
                vid = nlattr(nla_data(vids))
                rrem.value = nla_len(vids)
                while nla_ok(vid, rrem):
                    print('\t\t{}'.format(nla_get_u16(vid)))
                    vid = nla_next(vid, rrem)

    return NL_SKIP