示例#1
0
文件: types.py 项目: 0x90/libnl
 def __init__(self):
     """Constructor."""
     self.nm_protocol = 0
     self.nm_flags = 0
     self.nm_src = sockaddr_nl()
     self.nm_dst = sockaddr_nl()
     self.nm_creds = None
     self.nm_nlh = None
     self.nm_size = 0
     self.nm_refcnt = 1
示例#2
0
文件: types.py 项目: 0x90/libnl
 def __init__(self):
     """Constructor."""
     self.s_local = sockaddr_nl()
     self.s_peer = sockaddr_nl()
     self.s_proto = 0
     self.s_seq_next = 0
     self.s_seq_expect = 0
     self.s_flags = 0
     self.s_cb = None
     self.s_bufsize = None
     self.socket_instance = None
示例#3
0
def test_nl_recv():
    r"""C code to test against.

    // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && ./a.out
    #include <netlink/msg.h>
    struct nl_sock {
        struct sockaddr_nl s_local; struct sockaddr_nl s_peer; int s_fd; int s_proto; unsigned int s_seq_next;
        unsigned int s_seq_expect; int s_flags; struct nl_cb *s_cb; size_t s_bufsize;
    };
    int main() {
        // Send data to the kernel.
        struct nl_sock *sk = nl_socket_alloc();
        sk->s_seq_next = 0;
        nl_connect(sk, NETLINK_ROUTE);
        int ret = nl_send_simple(sk, 0, NLM_F_REQUEST, NULL, 0);
        printf("Bytes Sent: %d\n", ret);

        // Retrieve kernel's response.
        // nl_recvmsgs_default(sk);
        // return 0;
        unsigned char *buf = NULL;
        struct sockaddr_nl nla = {0};
        printf("%d == nla.nl_family\n", nla.nl_family);
        int n = nl_recv(sk, &nla, &buf, NULL);
        printf("Bytes Recv: %d\n", n);

        int i = 0; for (i = 0; i<n; i++) printf("%02x", buf[i]); printf("\n");
        printf("%d == nla.nl_family\n", nla.nl_family);
        return 0;
    }
    // Expected output:
    // Bytes Sent: 16
    // 0 == nla.nl_family
    // Bytes Recv: 36
    // 240000000200000000000000844c000000000000100000000000050000000000844c0000
    // 16 == nla.nl_family
    // Output delta:
    // 240000000200000000000000ac4e000000000000100000000000050000000000ac4e0000
    """
    sk = nl_socket_alloc()
    sk.s_seq_next = 0
    nl_connect(sk, NETLINK_ROUTE)
    assert 16 == nl_send_simple(sk, 0, NLM_F_REQUEST, None)

    buf = bytearray()
    nla = sockaddr_nl()
    assert 0 == nla.nl_family
    assert 36 == nl_recv(sk, nla, buf, None)
    nl_socket_free(sk)

    buf_hex = binascii.hexlify(buffer(buf)).decode('ascii')
    assert re.match(
        r'240000000200000000000000....000000000000100000000000050000000000....0000',
        buf_hex)
    assert 16 == nla.nl_family
示例#4
0
def test_nl_recv():
    r"""C code to test against.

    // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && ./a.out
    #include <netlink/msg.h>
    struct nl_sock {
        struct sockaddr_nl s_local; struct sockaddr_nl s_peer; int s_fd; int s_proto; unsigned int s_seq_next;
        unsigned int s_seq_expect; int s_flags; struct nl_cb *s_cb; size_t s_bufsize;
    };
    int main() {
        // Send data to the kernel.
        struct nl_sock *sk = nl_socket_alloc();
        sk->s_seq_next = 0;
        nl_connect(sk, NETLINK_ROUTE);
        int ret = nl_send_simple(sk, 0, NLM_F_REQUEST, NULL, 0);
        printf("Bytes Sent: %d\n", ret);

        // Retrieve kernel's response.
        // nl_recvmsgs_default(sk);
        // return 0;
        unsigned char *buf = NULL;
        struct sockaddr_nl nla = {0};
        printf("%d == nla.nl_family\n", nla.nl_family);
        int n = nl_recv(sk, &nla, &buf, NULL);
        printf("Bytes Recv: %d\n", n);

        int i = 0; for (i = 0; i<n; i++) printf("%02x", buf[i]); printf("\n");
        printf("%d == nla.nl_family\n", nla.nl_family);
        return 0;
    }
    // Expected output:
    // Bytes Sent: 16
    // 0 == nla.nl_family
    // Bytes Recv: 36
    // 240000000200000000000000844c000000000000100000000000050000000000844c0000
    // 16 == nla.nl_family
    // Output delta:
    // 240000000200000000000000ac4e000000000000100000000000050000000000ac4e0000
    """
    sk = nl_socket_alloc()
    sk.s_seq_next = 0
    nl_connect(sk, NETLINK_ROUTE)
    assert 16 == nl_send_simple(sk, 0, NLM_F_REQUEST, None)

    buf = bytearray()
    nla = sockaddr_nl()
    assert 0 == nla.nl_family
    assert 36 == nl_recv(sk, nla, buf, None)
    nl_socket_free(sk)

    buf_hex = binascii.hexlify(buffer(buf)).decode('ascii')
    assert re.match(r'240000000200000000000000....000000000000100000000000050000000000....0000', buf_hex)
    assert 16 == nla.nl_family
示例#5
0
文件: nl.py 项目: kimocoder/libnl-1
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
示例#6
0
文件: nl.py 项目: 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