def print_genl_msg(_, ofd, hdr, ops, payloadlen): """https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L831. Positional arguments: _ -- unused. ofd -- function to call with arguments similar to `logging.debug`. hdr -- Netlink message header (nlmsghdr class instance). ops -- cache operations (nl_cache_ops class instance). payloadlen -- length of payload in message (ctypes.c_int instance). Returns: data (bytearray_ptr). """ data = nlmsg_data(hdr) if payloadlen.value < GENL_HDRLEN: return data print_genl_hdr(ofd, data) payloadlen.value -= GENL_HDRLEN data = bytearray_ptr(data, GENL_HDRLEN) if ops: hdrsize = ops.co_hdrsize - GENL_HDRLEN if hdrsize > 0: if payloadlen.value < hdrsize: return data ofd(' [HEADER] %d octets', hdrsize) dump_hex(ofd, data, hdrsize, 0) payloadlen.value -= hdrsize data = bytearray_ptr(data, hdrsize) return data
def test_one_dimension_forbidden(): """Test single bytearray_obj with invalid operations.""" ba = bytearray_ptr(bytearray(b'The quick brown fox jumps over the lazy dog.')) ba[40:] = bytearray(b'\0\0\0\0') # Allowed because same length. ba[:-40] = bytearray(b'\0\0\0\0') # Allowed because same length. assert bytearray(b'\0\0\0\0quick brown fox jumps over the lazy \0\0\0\0') == bytearray(ba) with pytest.raises(AttributeError): ba.pop() with pytest.raises(AttributeError): ba.pop(40) with pytest.raises(TypeError): del ba[39] with pytest.raises(TypeError): del ba[37:] with pytest.raises(TypeError): del ba[31:35] with pytest.raises(TypeError): ba[40:] = bytearray(b'\0\0\0') with pytest.raises(TypeError): ba[:-40] = bytearray(b'\0\0\0\0\0') with pytest.raises(TypeError): ba += bytearray(b'more') ba.pop() # PyCharm inspection.
def test_two_dimension(): """Test nested bytearray_obj.""" pointee = bytearray(b'The quick brown fox jumps over the lazy dog.') ba = bytearray_ptr(pointee, 4, 15) assert bytearray(b'quick brown') == bytearray(ba) assert bytearray(b'quick brown') == bytearray(ba[:100]) assert bytearray(b'The quick brown fox jumps over the lazy dog.') == pointee ba[6:] = bytearray(b'apple') assert bytearray(b'quick apple') == bytearray(ba) assert bytearray(b'The quick apple fox jumps over the lazy dog.') == pointee ba[6:15] = bytearray(b'apple') assert bytearray(b'quick apple') == bytearray(ba) assert bytearray(b'The quick apple fox jumps over the lazy dog.') == pointee ba[6:11] = bytearray(b'green') assert bytearray(b'quick green') == bytearray(ba) assert bytearray(b'The quick green fox jumps over the lazy dog.') == pointee ba[5] = bytearray(b'_')[0] assert bytearray(b'quick_green') == bytearray(ba) assert bytearray(b'The quick_green fox jumps over the lazy dog.') == pointee ba[-2] = 0 assert bytearray(b'quick_gre\0n') == bytearray(ba) assert bytearray(b'The quick_gre\0n fox jumps over the lazy dog.') == pointee ba[-4:] = bytearray(b'\0\0\0\0') assert bytearray(b'quick_g\0\0\0\0') == bytearray(ba) assert bytearray(b'The quick_g\0\0\0\0 fox jumps over the lazy dog.') == pointee ba[1:-7] = bytearray(b'\0\0\0') assert bytearray(b'q\0\0\0k_g\0\0\0\0') == bytearray(ba) assert bytearray(b'The q\0\0\0k_g\0\0\0\0 fox jumps over the lazy dog.') == pointee ba[0:-9] = bytearray(b'AA') assert bytearray(b'AA\0\0k_g\0\0\0\0') == bytearray(ba) assert bytearray(b'The AA\0\0k_g\0\0\0\0 fox jumps over the lazy dog.') == pointee assert 11 == len(ba) assert 95 == ba[5] assert 103 in ba assert 66 not in ba with pytest.raises(IndexError): ba[11]() with pytest.raises(IndexError): ba[11] = 100 expected = [65, 65, 0, 0, 107, 95, 103, 0, 0, 0, 0] assert expected == list(ba) e_iter = iter(expected) for i in ba: assert next(e_iter) == i e_iter = iter(expected) for i in iter(ba): assert next(e_iter) == i e_iter = iter(reversed(expected)) for i in reversed(ba): assert next(e_iter) == i
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)
def dump_attrs(ofd, attrs, attrlen, prefix=0): """https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L869. Positional arguments: ofd -- function to call with arguments similar to `logging.debug`. attrs -- nlattr class instance. attrlen -- length of payload (integer). Keyword arguments: prefix -- additional number of whitespace pairs to prefix each log statement with. """ prefix_whitespaces = ' ' * prefix rem = c_int() for nla in nla_for_each_attr(attrs, attrlen, rem): alen = nla_len(nla) if nla.nla_type == 0: ofd('%s [ATTR PADDING] %d octets', prefix_whitespaces, alen) else: is_nested = ' NESTED' if nla_is_nested(nla) else '' ofd('%s [ATTR %02d%s] %d octets', prefix_whitespaces, nla.nla_type, is_nested, alen) if nla_is_nested(nla): dump_attrs(ofd, nla_data(nla), alen, prefix + 1) else: dump_attr(ofd, nla, prefix) padlen = nla_padlen(alen) if padlen > 0: ofd('%s [PADDING] %d octets', prefix_whitespaces, padlen) dump_hex(ofd, bytearray_ptr(nla_data(nla), alen), padlen, prefix) if rem.value: ofd('%s [LEFTOVER] %d octets', prefix_whitespaces, rem)
def nlmsg_tail(nlh): """https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L110. Positional arguments: nlh -- nlmsghdr class instance. Returns: bytearray_ptr instance after the last nla in the nlmsghdr. """ return bytearray_ptr(nlh.bytearray, NLMSG_ALIGN(nlh.nlmsg_len))
def nlmsg_data(nlh): """Return bytearray_ptr of the message payload. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L105 Positional arguments: nlh -- Netlink message header (nlmsghdr class instance). Returns: bytearray_ptr beginning with the start of the message payload. """ return bytearray_ptr(nlh.bytearray, NLMSG_HDRLEN)
def nla_data(nla): """Return bytearray_ptr of the payload data. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/attr.c#L120 Positional arguments: nla -- attribute (nlattr class instance). Returns: Bytearray payload data. """ return bytearray_ptr(nla.bytearray, NLA_HDRLEN)
def genlmsg_len(gnlh): """Return length of message payload including user header. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L224 Positional arguments: gnlh -- Generic Netlink message header (genlmsghdr class instance). Returns: Length of user payload including an eventual user header in number of bytes. """ nlh = nlmsghdr(bytearray_ptr(gnlh.bytearray, -NLMSG_HDRLEN, oob=True)) return nlh.nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN
def test_oob_negative(): """Test out of bounds slicing.""" origin = bytearray(b'!') * 30 parent = bytearray_ptr(origin, 5, -5) infant = bytearray_ptr(parent, 5, -5) offset = bytearray_ptr(infant, -3, oob=True) offset[:7] = bytearray(b'abcdefg') assert 13 == len(offset) assert 10 == len(infant) assert 20 == len(parent) assert 30 == len(origin) assert bytearray(b'abcdefg!!!!!!') == bytearray(offset) assert bytearray(b'defg!!!!!!') == bytearray(infant) assert bytearray(b'!!abcdefg!!!!!!!!!!!') == bytearray(parent) assert bytearray(b'!!!!!!!abcdefg!!!!!!!!!!!!!!!!') == bytearray(origin) offset = bytearray_ptr(infant, -9, oob=True) offset[:7] = bytearray(b'hijklmn') assert 19 == len(offset) assert 10 == len(infant) assert 20 == len(parent) assert 30 == len(origin) assert bytearray(b'hijklmnbcdefg!!!!!!') == bytearray(offset) assert bytearray(b'defg!!!!!!') == bytearray(infant) assert bytearray(b'lmnbcdefg!!!!!!!!!!!') == bytearray(parent) assert bytearray(b'!hijklmnbcdefg!!!!!!!!!!!!!!!!') == bytearray(origin) offset = bytearray_ptr(infant, 0, -3, oob=True) offset[:7] = bytearray(b'opqrstu') assert 7 == len(offset) assert 10 == len(infant) assert 20 == len(parent) assert 30 == len(origin) assert bytearray(b'opqrstu') == bytearray(offset) assert bytearray(b'opqrstu!!!') == bytearray(infant) assert bytearray(b'lmnbcopqrstu!!!!!!!!') == bytearray(parent) assert bytearray(b'!hijklmnbcopqrstu!!!!!!!!!!!!!') == bytearray(origin)
def nlmsg_reserve(n, len_, pad): """Reserve room for additional data in a Netlink message. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L407 Reserves room for additional data at the tail of the an existing netlink message. Eventual padding required will be zeroed out. bytearray_ptr() at the start of additional data or None. """ nlmsg_len_ = n.nm_nlh.nlmsg_len tlen = len_ if not pad else ((len_ + (pad - 1)) & ~(pad - 1)) if tlen + nlmsg_len_ > n.nm_size: return None buf = bytearray_ptr(n.nm_nlh.bytearray, nlmsg_len_) n.nm_nlh.nlmsg_len += tlen if tlen > len_: bytearray_ptr(buf, len_, tlen)[:] = bytearray(b'\0') * (tlen - len_) _LOGGER.debug('msg 0x%x: Reserved %d (%d) bytes, pad=%d, nlmsg_len=%d', id(n), tlen, len_, pad, n.nm_nlh.nlmsg_len) return buf
def genlmsg_user_hdr(gnlh): """Return bytearray_ptr of the user header. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L242 Calculates the bytearray_ptr of the user header based on the Generic Netlink message header. Positional arguments: gnlh -- Generic Netlink message header (genlmsghdr class instance). Returns: bytearray_ptr of the user header. """ return bytearray_ptr(gnlh.bytearray, GENL_HDRLEN)
def nlmsg_attrdata(nlh, hdrlen): """Head of attributes data. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L143 Positional arguments: nlh -- Netlink message header (nlmsghdr class instance). hdrlen -- length of family specific header (integer). Returns: First attribute (nlattr class instance with others in its payload). """ data = nlmsg_data(nlh) return libnl.linux_private.netlink.nlattr(bytearray_ptr(data, libnl.linux_private.netlink.NLMSG_ALIGN(hdrlen)))
def genlmsg_user_data(gnlh, hdrlen): """Return bytearray_ptr of the user data. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/genl/genl.c#L259 Calculates the bytearray_ptr of the user data based on the Generic Netlink message header. Positional arguments: gnlh -- Generic Netlink message header (genlmsghdr class instance). hdrlen -- length of user header (integer). Returns: bytearray_ptr of the user data. """ return bytearray_ptr(genlmsg_user_hdr(gnlh), NLMSG_ALIGN(hdrlen))
def nlmsg_next(nlh, remaining): """Next Netlink message in message stream. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/msg.c#L194 Positional arguments: nlh -- Netlink message header (nlmsghdr class instance). remaining -- number of bytes remaining in message stream (c_int). Returns: The next Netlink message in the message stream and decrements remaining by the size of the current message. """ totlen = libnl.linux_private.netlink.NLMSG_ALIGN(nlh.nlmsg_len) remaining.value -= totlen return libnl.linux_private.netlink.nlmsghdr(bytearray_ptr(nlh.bytearray, totlen))
def nla_next(nla, remaining): """Return next attribute in a stream of attributes. https://github.com/thom311/libnl/blob/libnl3_2_25/lib/attr.c#L171 Calculates the offset to the next attribute based on the attribute given. The attribute provided is assumed to be accessible, the caller is responsible to use nla_ok() beforehand. The offset (length of specified attribute including padding) is then subtracted from the remaining bytes variable and a pointer to the next attribute is returned. nla_next() can be called as long as remaining is >0. Positional arguments: nla -- attribute of any kind (nlattr class instance). remaining -- number of bytes remaining in attribute stream (c_int). Returns: Next nlattr class instance. """ totlen = int(NLA_ALIGN(nla.nla_len)) remaining.value -= totlen return nlattr(bytearray_ptr(nla.bytearray, totlen))
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 test_three_dimension(): """Test double-nested bytearray_obj.""" origin = bytearray(b"All letters: The quick brown fox jumps over the lazy dog.\nIsn't it cool?") pointee = bytearray_ptr(origin, 13, -15) ba = bytearray_ptr(pointee, 4, 15) assert hasattr(ba, 'pointee') assert hasattr(pointee, 'pointee') assert not hasattr(ba.pointee, 'pointee') assert bytearray(b'quick brown') == bytearray(ba) assert bytearray(b'quick brown') == bytearray(ba[:100]) assert bytearray(b'The quick brown fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick brown fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[6:] = bytearray(b'apple') assert bytearray(b'quick apple') == bytearray(ba) assert bytearray(b'The quick apple fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick apple fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[6:15] = bytearray(b'apple') assert bytearray(b'quick apple') == bytearray(ba) assert bytearray(b'The quick apple fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick apple fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[6:11] = bytearray(b'green') assert bytearray(b'quick green') == bytearray(ba) assert bytearray(b'The quick green fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick green fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[5] = bytearray(b'_')[0] assert bytearray(b'quick_green') == bytearray(ba) assert bytearray(b'The quick_green fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick_green fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[-2] = 0 assert bytearray(b'quick_gre\0n') == bytearray(ba) assert bytearray(b'The quick_gre\0n fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick_gre\0n fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[-4:] = bytearray(b'\0\0\0\0') assert bytearray(b'quick_g\0\0\0\0') == bytearray(ba) assert bytearray(b'The quick_g\0\0\0\0 fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The quick_g\0\0\0\0 fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[1:-7] = bytearray(b'\0\0\0') assert bytearray(b'q\0\0\0k_g\0\0\0\0') == bytearray(ba) assert bytearray(b'The q\0\0\0k_g\0\0\0\0 fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The q\0\0\0k_g\0\0\0\0 fox jumps over the lazy dog.\nIsn't it cool?") == origin ba[0:-9] = bytearray(b'AA') assert bytearray(b'AA\0\0k_g\0\0\0\0') == bytearray(ba) assert bytearray(b'The AA\0\0k_g\0\0\0\0 fox jumps over the lazy dog.') == bytearray(pointee) assert bytearray(b"All letters: The AA\0\0k_g\0\0\0\0 fox jumps over the lazy dog.\nIsn't it cool?") == origin assert 11 == len(ba) assert 95 == ba[5] assert 103 in ba assert 66 not in ba with pytest.raises(IndexError): ba[11]() with pytest.raises(IndexError): ba[11] = 100 expected = [65, 65, 0, 0, 107, 95, 103, 0, 0, 0, 0] assert expected == list(ba) e_iter = iter(expected) for i in ba: assert next(e_iter) == i e_iter = iter(expected) for i in iter(ba): assert next(e_iter) == i e_iter = iter(reversed(expected)) for i in reversed(ba): assert next(e_iter) == i
def rta_type(self, value): """Type setter.""" self.bytearray[self._get_slicers(1)] = bytearray(c_ushort(value or 0)) @property def payload(self): """Payload and padding at the end.""" return self.bytearray[self._get_slicers(1).stop:] RTA_ALIGNTO = 4 RTA_ALIGN = lambda len_: (len_ + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1) RTA_OK = lambda rta, len_: len_.value >= rtattr.SIZEOF and rtattr.SIZEOF <= rta.rta_len <= len_.value RTA_NEXT = lambda rta, attrlen: ( setattr(attrlen, 'value', attrlen.value - RTA_ALIGN(rta.rta_len)), rtattr(bytearray_ptr(rta.bytearray, RTA_ALIGN(rta.rta_len))))[1] RTA_LENGTH = lambda len_: RTA_ALIGN(rtattr.SIZEOF) + len_ RTA_SPACE = lambda len_: RTA_ALIGN(RTA_LENGTH(len_)) RTA_DATA = lambda rta: rta.payload.rstrip(b'\0') RTA_PAYLOAD = lambda rta: rta.rta_len - RTA_LENGTH(0) class rtgenmsg(Struct): """https://github.com/thom311/libnl/blob/libnl3_2_25/include/linux/rtnetlink.h#L410. Instance variables: rtgen_family -- rtgen family (c_ubyte). """ _REPR = '<{0}.{1} rtgen_family={2[rtgen_family]}>' SIGNATURE = (SIZEOF_UBYTE, )
def payload(self): """Payload and padding at the end (bytearray_ptr).""" return bytearray_ptr(self.bytearray, self._get_slicers(2).stop)
IFLA_MASTER = 10 IFLA_WIRELESS = 11 IFLA_PROTINFO = 12 IFLA_TXQLEN = 13 IFLA_MAP = 14 IFLA_WEIGHT = 15 IFLA_OPERSTATE = 16 IFLA_LINKMODE = 17 IFLA_LINKINFO = 18 IFLA_NET_NS_PID = 19 IFLA_IFALIAS = 20 IFLA_NUM_VF = 21 IFLA_VFINFO_LIST = 22 IFLA_STATS64 = 23 IFLA_VF_PORTS = 24 IFLA_PORT_SELF = 25 IFLA_AF_SPEC = 26 IFLA_GROUP = 27 IFLA_NET_NS_FD = 28 IFLA_EXT_MASK = 29 IFLA_PROMISCUITY = 30 IFLA_NUM_TX_QUEUES = 31 IFLA_NUM_RX_QUEUES = 32 IFLA_CARRIER = 33 IFLA_PHYS_PORT_ID = 34 IFLA_CARRIER_CHANGES = 35 IFLA_MAX = IFLA_CARRIER_CHANGES IFLA_RTA = lambda r: rtattr(bytearray_ptr(r.bytearray, NLMSG_ALIGN(ifinfomsg.SIZEOF)))
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
IFLA_MASTER = 10 IFLA_WIRELESS = 11 IFLA_PROTINFO = 12 IFLA_TXQLEN = 13 IFLA_MAP = 14 IFLA_WEIGHT = 15 IFLA_OPERSTATE = 16 IFLA_LINKMODE = 17 IFLA_LINKINFO = 18 IFLA_NET_NS_PID = 19 IFLA_IFALIAS = 20 IFLA_NUM_VF = 21 IFLA_VFINFO_LIST = 22 IFLA_STATS64 = 23 IFLA_VF_PORTS = 24 IFLA_PORT_SELF = 25 IFLA_AF_SPEC = 26 IFLA_GROUP = 27 IFLA_NET_NS_FD = 28 IFLA_EXT_MASK = 29 IFLA_PROMISCUITY = 30 IFLA_NUM_TX_QUEUES = 31 IFLA_NUM_RX_QUEUES = 32 IFLA_CARRIER = 33 IFLA_PHYS_PORT_ID = 34 IFLA_CARRIER_CHANGES = 35 IFLA_MAX = IFLA_CARRIER_CHANGES IFLA_RTA = lambda r: rtattr( bytearray_ptr(r.bytearray, NLMSG_ALIGN(ifinfomsg.SIZEOF)))
def payload(self): """Payload and padding at the end (bytearray_ptr).""" return bytearray_ptr(self.bytearray, self._get_slicers(5).stop)
@rta_type.setter def rta_type(self, value): """Type setter.""" self.bytearray[self._get_slicers(1)] = bytearray(c_ushort(value or 0)) @property def payload(self): """Payload and padding at the end.""" return self.bytearray[self._get_slicers(1).stop:] RTA_ALIGNTO = 4 RTA_ALIGN = lambda len_: (len_ + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1) RTA_OK = lambda rta, len_: len_.value >= rtattr.SIZEOF and rtattr.SIZEOF <= rta.rta_len <= len_.value RTA_NEXT = lambda rta, attrlen: (setattr(attrlen, 'value', attrlen.value - RTA_ALIGN(rta.rta_len)), rtattr(bytearray_ptr(rta.bytearray, RTA_ALIGN(rta.rta_len))))[1] RTA_LENGTH = lambda len_: RTA_ALIGN(rtattr.SIZEOF) + len_ RTA_SPACE = lambda len_: RTA_ALIGN(RTA_LENGTH(len_)) RTA_DATA = lambda rta: rta.payload.rstrip(b'\0') RTA_PAYLOAD = lambda rta: rta.rta_len - RTA_LENGTH(0) class rtgenmsg(Struct): """https://github.com/thom311/libnl/blob/libnl3_2_25/include/linux/rtnetlink.h#L410. Instance variables: rtgen_family -- rtgen family (c_ubyte). """ _REPR = '<{0}.{1} rtgen_family={2[rtgen_family]}>' SIGNATURE = (SIZEOF_UBYTE, )