def main(): """Main function called upon script execution.""" # First open a socket to the kernel. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. ret = nl_connect(sk, NETLINK_ROUTE) # Create file descriptor and bind socket. if ret < 0: reason = errmsg[abs(ret)] return error('nl_connect() returned {0} ({1})'.format(ret, reason)) # Next we send the request to the kernel. rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) if ret < 0: reason = errmsg[abs(ret)] return error('nl_send_simple() returned {0} ({1})'.format(ret, reason)) print('Sent {0} bytes to the kernel.'.format(ret)) # Finally we'll retrieve the kernel's answer, process it, and call any callbacks attached to the `nl_sock` instance. nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, None) # Add callback to the `nl_sock` instance. ret = nl_recvmsgs_default( sk) # Get kernel's answer, and call attached callbacks. if ret < 0: reason = errmsg[abs(ret)] return error('nl_recvmsgs_default() returned {0} ({1})'.format( ret, reason))
def ncsi_set_interface(ifindex, package, channel): sk = nl_socket_alloc() ret = genl_connect(sk) if ret < 0: return ret driver_id = genl_ctrl_resolve(sk, b'NCSI') if driver_id < 0: return driver_id msg = nlmsg_alloc() if package or package and channel: genlmsg_put(msg, 0, 0, driver_id, 0, 0, NCSI_CMD_SET_INTERFACE, 0) ret = nla_put_u32(msg, NCSI_ATTR_PACKAGE_ID, int(package)) if channel: ret = nla_put_u32(msg, NCSI_ATTR_CHANNEL_ID, int(channel)) else: genlmsg_put(msg, 0, 0, driver_id, 0, 0, NCSI_CMD_CLEAR_INTERFACE, 0) ret = nla_put_u32(msg, NCSI_ATTR_IFINDEX, ifindex) nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback, None) ret = nl_send_auto(sk, msg) if ret < 0: print("Failed to send message: {}".format(ret)) return ret ret = nl_recvmsgs_default(sk) # blocks if ret < 0: reason = errmsg[abs(ret)] print("recvmsg returned {}, {}".format(ret, reason))
def test_s_proto(): """Test socket protocol.""" sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) msg = nlmsg_alloc_simple(0, 0) assert -1 == msg.nm_protocol nl_complete_msg(sk, msg) assert NETLINK_ROUTE == msg.nm_protocol nl_socket_free(sk)
def test_nm_protocol(): """Test protocol.""" sk = nl_socket_alloc() msg = nlmsg_alloc_simple(0, 0) assert -1 == msg.nm_protocol msg.nm_protocol = 10 nl_complete_msg(sk, msg) assert 10 == msg.nm_protocol nl_socket_free(sk)
def test_nlmsg_pid(): """Test pid.""" sk = nl_socket_alloc() msg = nlmsg_alloc_simple(0, 0) assert 0 == msg.nm_nlh.nlmsg_pid msg.nm_nlh.nlmsg_pid = 10 nl_complete_msg(sk, msg) assert 10 == msg.nm_nlh.nlmsg_pid nl_socket_free(sk)
def scan_for_wifi_networks(interface): def db_to_percent(db): return 2.0 * (db + 100) pack = struct.pack('16sI', interface, 0) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack)) except OSError: raise RuntimeError( 'Wireless interface {0} does not exist.'.format(interface)) finally: sk.close() if_index = int(info[1]) # Next open a socket to the kernel and bind to it. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. if not genl_connect(sk) == 0: # Create file descriptor and bind socket. raise RuntimeError('Could not communicate with kernel via nl80211') driver_id = genl_ctrl_resolve(sk, b'nl80211') mcid = genl_ctrl_resolve_grp(sk, b'nl80211', b'scan') results = dict() for i in range(2, -1, -1): # Three tries on errors. ret = do_scan_trigger(sk, if_index, driver_id, mcid) if ret < 0: #timeout time.sleep(5) continue ret = do_scan_results(sk, if_index, driver_id, results) if ret < 0: time.sleep(5) continue break if not results: return {} ap_list = [] for _, f in results.items(): ssid = str(f['information_elements']['SSID']).encode("ascii", "ignore") if len(ssid) == 0: continue if u"\x00".encode("ascii", "ignore") in ssid: continue print(ssid, len(ssid)) # pp.pprint(f['information_elements']) security = 0 if 'RSN' in f['information_elements']: security = 1 signal_strength = db_to_percent(f['signal']) if signal_strength <= 25: continue network_tuple = [ssid, security, signal_strength] ap_list.append(network_tuple) return ap_list
def scan_all_access_points(self): # Scan for access points within reach # First get the wireless interface index. pack = struct.pack('16sI', self.interface_name.encode('ascii'), 0) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sock.fileno(), 0x8933, pack)) except OSError: return logger.warning('Wireless interface {0}\ does not exist.'.format(self.interface_name)) finally: sock.close() if_index = int(info[1]) # Next open a socket to the kernel and bind to it. Same one used for # sending and receiving. self._nl_sock = nl_socket_alloc() # Creates an `nl_sock` instance. genl_connect(self._nl_sock) # Create file descriptor and bind socket. logger.debug('Finding the nl80211 driver ID...') driver_id = genl_ctrl_resolve(self._nl_sock, b'nl80211') logger.debug('Finding the nl80211 scanning group ID...') mcid = genl_ctrl_resolve_grp(self._nl_sock, b'nl80211', b'scan') # Scan for access points 1 or more (if requested) times. results = dict() for i in range(2, -1, -1): # Three tries on errors. ret = self._do_scan_trigger(if_index, driver_id, mcid) if ret < 0: logger.debug('do_scan_trigger() returned {0},' 'retrying in 5 seconds({1}).'.format(ret, i)) time.sleep(5) ret = self._do_scan_results(if_index, driver_id, results) if ret < 0: logger.debug('do_scan_results() returned {0},' 'retrying in 5 seconds ({1}).'.format(ret, i)) time.sleep(5) continue break if not results: logger.debug('No access points detected.') return [] logger.debug('Found {0} access points:'.format(len(results))) # Convert timedelta to integer to avoid # TypeError: Object of type timedelta is not JSON serializable for ap in results: for prop in results[ap]: if isinstance(results[ap][prop], datetime.timedelta): results[ap][prop] = int( results[ap][prop].microseconds) / 1000 return results
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
def main(): """Main function called upon script execution.""" # First get the wireless interface index. pack = struct.pack('16sI', OPTIONS['<interface>'].encode('ascii'), 0) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack)) except OSError: return error('Wireless interface {0} does not exist.'.format(OPTIONS['<interface>'])) finally: sk.close() if_index = int(info[1]) # Next open a socket to the kernel and bind to it. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. ok(0, genl_connect, sk) # Create file descriptor and bind socket. _LOGGER.debug('Finding the nl80211 driver ID...') driver_id = ok(0, genl_ctrl_resolve, sk, b'nl80211') _LOGGER.debug('Finding the nl80211 scanning group ID...') mcid = ok(0, genl_ctrl_resolve_grp, sk, b'nl80211', b'scan') # Scan for access points 1 or more (if requested) times. if not OPTIONS['--no-sudo']: print('Scanning for access points, may take about 8 seconds...') else: print("Attempting to read results of previous scan.") results = dict() for i in range(2, -1, -1): # Three tries on errors. if not OPTIONS['--no-sudo']: ret = ok(i, do_scan_trigger, sk, if_index, driver_id, mcid) if ret < 0: _LOGGER.warning('do_scan_trigger() returned %d, retrying in 5 seconds.', ret) time.sleep(5) continue ret = ok(i, do_scan_results, sk, if_index, driver_id, results) if ret < 0: _LOGGER.warning('do_scan_results() returned %d, retrying in 5 seconds.', ret) time.sleep(5) continue break if not results: print('No access points detected.') return # Print results. print('Found {0} access points:'.format(len(results))) print_table(results.values()) best_channel = get_best_channel(results) print("Best Channel: {}".format(best_channel)) if best_channel: build_hostapd_config(best_channel) sys.exit(0) else: sys.exit(1)
def update_iface_details(self, cmd): # Send a command specified by CMD to the kernel and attach a callback to # process the returned values into our own datastructure self._nl_sock = nl_socket_alloc() # Creates an `nl_sock` instance. # Create file descriptor and bind socket. ret = genl_connect(self._nl_sock) if ret < 0: reason = errmsg[abs(ret)] logger.error('genl_connect() returned {0} ({1})'.format( ret, reason)) return {} # Now get the nl80211 driver ID. Handle errors here. # Find the nl80211 driver ID. driver_id = genl_ctrl_resolve(self._nl_sock, b'nl80211') if driver_id < 0: reason = errmsg[abs(driver_id)] logger.error('genl_ctrl_resolve() returned {0} ({1})'.format( driver_id, reason)) return {} # Setup the Generic Netlink message. msg = nlmsg_alloc() # Allocate a message. if self.if_idx == None: # Ask kernel to send info for all wireless interfaces. genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, nl80211.NL80211_CMD_GET_INTERFACE, 0) else: genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, cmd, 0) # This is the interface we care about. nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, self.if_idx) #nla_put_u32(msg, nl80211.NL80211_ATTR_MAC, 2199023255552) # Add the callback function to the self._nl_sock. nl_socket_modify_cb(self._nl_sock, NL_CB_VALID, NL_CB_CUSTOM, self._iface_callback, False) # Now send the message to the kernel, and get its response, # automatically calling the callback. ret = nl_send_auto(self._nl_sock, msg) if ret < 0: reason = errmsg[abs(ret)] logger.error('nl_send_auto() returned {0} ({1})'.format( ret, reason)) return {} logger.debug('Sent {0} bytes to the kernel.'.format(ret)) # Blocks until the kernel replies. Usually it's instant. ret = nl_recvmsgs_default(self._nl_sock) if ret < 0: reason = errmsg[abs(ret)] logger.error('nl_recvmsgs_default() returned {0} ({1})'.format( ret, reason)) return {}
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
def test_dissect(monkeypatch): r"""Diff of C code (from test_bare()) to test against. --- test_bare.c 2015-02-08 12:43:15.543135855 -0800 +++ test_dissect.c 2015-02-08 13:25:31.375715668 -0800 @@ -16,8 +16,22 @@ sk->s_local.nl_pid = 0; sk->s_seq_next = 0; - int ret = nl_send_simple(sk, RTM_GETLINK, 0, NULL, 0); + struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; + struct nl_msg *msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP); + nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + nl_complete_msg(sk, msg); + struct nlmsghdr *nlh = nlmsg_hdr(msg); + printf("%d == nlh->nlmsg_len\n", nlh->nlmsg_len); + printf("%d == nlh->nlmsg_type\n", nlh->nlmsg_type); + printf("%d == nlh->nlmsg_flags\n", nlh->nlmsg_flags); + printf("%d == nlh->nlmsg_seq\n", nlh->nlmsg_seq); + printf("%d == nlh->nlmsg_pid\n", nlh->nlmsg_pid); + + struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len }; + struct msghdr hdr = { .msg_iov = &iov, .msg_iovlen = 1, }; + + int ret = nl_sendmsg(sk, msg, &hdr); printf("Bytes: %d\n", ret); return 0; } """ monkeypatch.setattr(libnl.socket_, 'generate_local_port', lambda: 0) sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.s_local.nl_pid = 0 sk.s_seq_next = 0 rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP) nlmsg_append(msg, rt_hdr, rt_hdr.SIZEOF, NLMSG_ALIGNTO) nl_complete_msg(sk, msg) nlh = nlmsg_hdr(msg) assert 20 == nlh.nlmsg_len assert 18 == nlh.nlmsg_type assert 773 == nlh.nlmsg_flags assert 0 == nlh.nlmsg_seq assert 0 == nlh.nlmsg_pid assert b'FAAAABIABQMAAAAAAAAAABEAAAA=' == base64.b64encode( buffer(nlh.bytearray[:nlh.nlmsg_len])) nl_socket_free(sk)
def test_dissect(monkeypatch): r"""Diff of C code (from test_bare()) to test against. --- test_bare.c 2015-02-08 12:43:15.543135855 -0800 +++ test_dissect.c 2015-02-08 13:25:31.375715668 -0800 @@ -16,8 +16,22 @@ sk->s_local.nl_pid = 0; sk->s_seq_next = 0; - int ret = nl_send_simple(sk, RTM_GETLINK, 0, NULL, 0); + struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; + struct nl_msg *msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP); + nlmsg_append(msg, &rt_hdr, sizeof(rt_hdr), NLMSG_ALIGNTO); + nl_complete_msg(sk, msg); + struct nlmsghdr *nlh = nlmsg_hdr(msg); + printf("%d == nlh->nlmsg_len\n", nlh->nlmsg_len); + printf("%d == nlh->nlmsg_type\n", nlh->nlmsg_type); + printf("%d == nlh->nlmsg_flags\n", nlh->nlmsg_flags); + printf("%d == nlh->nlmsg_seq\n", nlh->nlmsg_seq); + printf("%d == nlh->nlmsg_pid\n", nlh->nlmsg_pid); + + struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len }; + struct msghdr hdr = { .msg_iov = &iov, .msg_iovlen = 1, }; + + int ret = nl_sendmsg(sk, msg, &hdr); printf("Bytes: %d\n", ret); return 0; } """ monkeypatch.setattr(libnl.socket_, 'generate_local_port', lambda: 0) sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.s_local.nl_pid = 0 sk.s_seq_next = 0 rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP) nlmsg_append(msg, rt_hdr, rt_hdr.SIZEOF, NLMSG_ALIGNTO) nl_complete_msg(sk, msg) nlh = nlmsg_hdr(msg) assert 20 == nlh.nlmsg_len assert 18 == nlh.nlmsg_type assert 773 == nlh.nlmsg_flags assert 0 == nlh.nlmsg_seq assert 0 == nlh.nlmsg_pid assert b'FAAAABIABQMAAAAAAAAAABEAAAA=' == base64.b64encode(buffer(nlh.bytearray[:nlh.nlmsg_len])) nl_socket_free(sk)
def main(): """Main function called upon script execution.""" # First get the wireless interface index. if OPTIONS['<interface>']: pack = struct.pack('16sI', OPTIONS['<interface>'].encode('ascii'), 0) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack)) except OSError: return error('Wireless interface {0} does not exist.'.format(OPTIONS['<interface>'])) finally: sk.close() if_index = int(info[1]) else: if_index = -1 # Then open a socket to the kernel. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. ret = genl_connect(sk) # Create file descriptor and bind socket. if ret < 0: reason = errmsg[abs(ret)] return error('genl_connect() returned {0} ({1})'.format(ret, reason)) # Now get the nl80211 driver ID. Handle errors here. driver_id = genl_ctrl_resolve(sk, b'nl80211') # Find the nl80211 driver ID. if driver_id < 0: reason = errmsg[abs(driver_id)] return error('genl_ctrl_resolve() returned {0} ({1})'.format(driver_id, reason)) # Setup the Generic Netlink message. msg = nlmsg_alloc() # Allocate a message. if OPTIONS['<interface>']: genlmsg_put(msg, 0, 0, driver_id, 0, 0, nl80211.NL80211_CMD_GET_INTERFACE, 0) # Tell kernel: send iface info. nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index) # This is the interface we care about. else: # Ask kernel to send info for all wireless interfaces. genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, nl80211.NL80211_CMD_GET_INTERFACE, 0) # Add the callback function to the nl_sock. has_printed = list() nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, has_printed) # Now send the message to the kernel, and get its response, automatically calling the callback. ret = nl_send_auto(sk, msg) if ret < 0: reason = errmsg[abs(ret)] return error('nl_send_auto() returned {0} ({1})'.format(ret, reason)) print('Sent {0} bytes to the kernel.'.format(ret)) ret = nl_recvmsgs_default(sk) # Blocks until the kernel replies. Usually it's instant. if ret < 0: reason = errmsg[abs(ret)] return error('nl_recvmsgs_default() returned {0} ({1})'.format(ret, reason))
def test_error_nle_bad_sock(): """Test for NLE_BAD_SOCK.""" sk = nl_socket_alloc() msg = nlmsg_alloc_simple(0, 0) nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() nl_complete_msg(sk, msg) message = 'Hello World!\n' iov = bytes(message.encode('ascii')) hdr = msghdr(msg_iov=iov) assert -3 == nl_sendmsg(sk, msg, hdr)
def test_default(tcp_server): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) // (nc -l 2000 &); sleep 0.1; ./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() { struct sockaddr_in sin = { .sin_port = htons(2000), .sin_family = AF_INET, }; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); int fd = socket(AF_INET, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &sin, sizeof(sin)); struct nl_sock *sk = nl_socket_alloc(); struct nl_msg *msg = nlmsg_alloc_simple(0, 0); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; sk->s_local.nl_pid = 0; nl_complete_msg(sk, msg); char message[] = "Hello World!\n"; struct iovec iov = { .iov_base = message, .iov_len = sizeof(message), }; struct msghdr hdr = { .msg_iov = &iov, .msg_iovlen = 1, }; int ret = nl_sendmsg(sk, msg, &hdr); printf("Bytes: %d\n", ret); // 14 return 0; } // Expected bash output: // Hello World! // Bytes: 14 """ sk = nl_socket_alloc() msg = nlmsg_alloc_simple(0, 0) nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 0 nl_complete_msg(sk, msg) message = 'Hello World!\n\0' iov = bytes(message.encode('ascii')) hdr = msghdr(msg_iov=iov) assert 14 == nl_sendmsg(sk, msg, hdr) assert [iov] == tcp_server.data nl_socket_free(sk)
def config_function(config): # Global variables storing conf and shared ressources global INTERFACE global CLIENTS global INTERFACEINDEX global VALUES global DRIVER_ID global SOCKET for node in config.children: key = node.key.lower() value = node.values[0] # Save Wi-Fi interface name if key == 'interface': INTERFACE = value collectd.info("Collecting stats for interface %s" % value) # Save a client MAC address elif key == 'client': CLIENTS.append(value) else: collectd.info("Unknown configuration key %s" % key) # No clients configured ? Store everything if len(CLIENTS) == 0: collectd.info("Collecting detailed stats for all clients") else: str_clients = "" for client in CLIENTS: str_clients = str_clients + " " + client collectd.info("Collecting stats for clients%s" % str_clients) # No Wi-FI interface configured if INTERFACE == "": collectd.error("No interface set in configuration") exit(1) else: # Get the interface index from the interface name pack = struct.pack('16sI', INTERFACE, 0) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack)) INTERFACEINDEX = int(info[1]) except IOError: collectd.error("Unknown network interface !") exit(1) # Create the netlink socket and resolve the driver id of extension nl80211 SOCKET = nl_socket_alloc() genl_connect(SOCKET) DRIVER_ID = genl_ctrl_resolve(SOCKET, b'nl80211')
def main(): """Main function called upon script execution.""" # First get the wireless interface index. pack = struct.pack('16sI', OPTIONS['<interface>'].encode('ascii'), 0) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack)) except OSError: return error('Wireless interface {0} does not exist.'.format(OPTIONS['<interface>'])) finally: sk.close() if_index = int(info[1]) # Next open a socket to the kernel and bind to it. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. ok(0, genl_connect, sk) # Create file descriptor and bind socket. _LOGGER.debug('Finding the nl80211 driver ID...') driver_id = ok(0, genl_ctrl_resolve, sk, b'nl80211') _LOGGER.debug('Finding the nl80211 scanning group ID...') mcid = ok(0, genl_ctrl_resolve_grp, sk, b'nl80211', b'scan') # Scan for access points 1 or more (if requested) times. if not OPTIONS['--no-sudo']: print('Scanning for access points, may take about 8 seconds...') else: print("Attempting to read results of previous scan.") results = dict() for i in range(2, -1, -1): # Three tries on errors. if not OPTIONS['--no-sudo']: ret = ok(i, do_scan_trigger, sk, if_index, driver_id, mcid) if ret < 0: _LOGGER.warning('do_scan_trigger() returned %d, retrying in 5 seconds.', ret) time.sleep(5) continue ret = ok(i, do_scan_results, sk, if_index, driver_id, results) if ret < 0: _LOGGER.warning('do_scan_results() returned %d, retrying in 5 seconds.', ret) time.sleep(5) continue break if not results: print('No access points detected.') return # Print results. print('Found {0} access points:'.format(len(results))) print_table(results.values())
def test_bare(tcp_server, monkeypatch): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && (nc -l 2000 |base64 &) && sleep 0.1 && ./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() { struct sockaddr_in sin = { .sin_port = htons(2000), .sin_family = AF_INET, }; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); int fd = socket(AF_INET, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &sin, sizeof(sin)); struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; sk->s_local.nl_pid = 0; sk->s_seq_next = 0; int ret = nl_send_simple(sk, RTM_GETLINK, 0, NULL, 0); printf("Bytes: %d\n", ret); return 0; } // Expected bash output: // Bytes: 16 // EAAAABIABQAAAAAAAAAAAA== """ monkeypatch.setattr(libnl.socket_, 'generate_local_port', lambda: 0) sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 0 sk.s_seq_next = 0 assert 16 == nl_send_simple(sk, RTM_GETLINK, 0, None) assert 1 == len(tcp_server.data) assert b'EAAAABIABQAAAAAAAAAAAA==' == base64.b64encode( buffer(tcp_server.data[0])) nl_socket_free(sk)
def test_bare(tcp_server, monkeypatch): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && (nc -l 2000 |base64 &) && sleep 0.1 && ./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() { struct sockaddr_in sin = { .sin_port = htons(2000), .sin_family = AF_INET, }; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); int fd = socket(AF_INET, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &sin, sizeof(sin)); struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; sk->s_local.nl_pid = 0; sk->s_seq_next = 0; int ret = nl_send_simple(sk, RTM_GETLINK, 0, NULL, 0); printf("Bytes: %d\n", ret); return 0; } // Expected bash output: // Bytes: 16 // EAAAABIABQAAAAAAAAAAAA== """ monkeypatch.setattr(libnl.socket_, 'generate_local_port', lambda: 0) sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 0 sk.s_seq_next = 0 assert 16 == nl_send_simple(sk, RTM_GETLINK, 0, None) assert 1 == len(tcp_server.data) assert b'EAAAABIABQAAAAAAAAAAAA==' == base64.b64encode(buffer(tcp_server.data[0])) nl_socket_free(sk)
def ncsi_get_info(ifindex, package): # Open socket to kernel sk = nl_socket_alloc() ret = genl_connect(sk) if ret < 0: print("Failed to open socket") return -1 # Find NCSI driver_id = genl_ctrl_resolve(sk, b'NCSI') if driver_id < 0: print("Could not resolve NCSI") return -1 # Setup up a Generic Netlink message msg = nlmsg_alloc() if package is None: ret = genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, NCSI_CMD_PKG_INFO, 0) else: ret = genlmsg_put(msg, 0, 0, driver_id, 0, 0, NCSI_CMD_PKG_INFO, 0) nla_put_u32(msg, NCSI_ATTR_PACKAGE_ID, int(package)) if ret < 0: reason = errmsg[abs(ret)] print("genlmsg_put returned {}, {}".format(ret, reason)) return -1 nla_put_u32(msg, NCSI_ATTR_IFINDEX, ifindex) # Add a callback function to the socket nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, info_callback, None) ret = nl_send_auto(sk, msg) if ret < 0: print("Failed to send message: {}".format(ret)) return ret ret = nl_recvmsgs_default(sk) # blocks if ret < 0: reason = errmsg[abs(ret)] print("recvmsg returned {}, {}".format(ret, reason))
def test_seq(): """Test sequences.""" msg = nlmsg_alloc() nlh = nlmsg_hdr(msg) assert 16 == nlh.nlmsg_len assert 0 == nlh.nlmsg_type assert 0 == nlh.nlmsg_flags assert 0 == nlh.nlmsg_seq assert 0 == nlh.nlmsg_pid sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.s_local.nl_pid = 0 nl_complete_msg(sk, msg) assert 1423351063 <= nlh.nlmsg_seq nlh.nlmsg_seq = 1423350947 assert 1423350947 == nlh.nlmsg_seq nlh.nlmsg_pid = 0 # sk.s_local.nl_pid is read-only in Python. assert b'EAAAAAAABQCjnNZUAAAAAA==' == base64.b64encode(buffer(nlh.bytearray[:nlh.nlmsg_len]))
def test_seq(): """Test sequences.""" msg = nlmsg_alloc() nlh = nlmsg_hdr(msg) assert 16 == nlh.nlmsg_len assert 0 == nlh.nlmsg_type assert 0 == nlh.nlmsg_flags assert 0 == nlh.nlmsg_seq assert 0 == nlh.nlmsg_pid sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.s_local.nl_pid = 0 nl_complete_msg(sk, msg) assert 1423351063 <= nlh.nlmsg_seq nlh.nlmsg_seq = 1423350947 assert 1423350947 == nlh.nlmsg_seq nlh.nlmsg_pid = 0 # sk.s_local.nl_pid is read-only in Python. assert b'EAAAAAAABQCjnNZUAAAAAA==' == base64.b64encode( buffer(nlh.bytearray[:nlh.nlmsg_len]))
def test_full(tcp_server, monkeypatch): r"""Diff of C code (from test_bare()) to test against. --- test_bare.c 2015-02-08 12:43:15.543135855 -0800 +++ test_full.c 2015-02-08 12:43:08.533183752 -0800 @@ -13,10 +13,11 @@ struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; - sk->s_local.nl_pid = 0; - sk->s_seq_next = 0; + sk->s_local.nl_pid = 1234; + sk->s_seq_next = 10; - int ret = nl_send_simple(sk, RTM_GETLINK, 0, NULL, 0); + struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; + int ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); printf("Bytes: %d\n", ret); return 0; """ monkeypatch.setattr(libnl.socket_, 'generate_local_port', lambda: 1234) sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 1234 sk.s_seq_next = 10 rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert 1 == len(tcp_server.data) assert b'FAAAABIABQMKAAAA0gQAABEAAAA=' == base64.b64encode( buffer(tcp_server.data[0])) nl_socket_free(sk)
def main(): """Main function called upon script execution.""" # First open a socket to the kernel. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. ret = nl_connect(sk, NETLINK_ROUTE) # Create file descriptor and bind socket. if ret < 0: reason = errmsg[abs(ret)] return error('nl_connect() returned {0} ({1})'.format(ret, reason)) # Next we send the request to the kernel. rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) if ret < 0: reason = errmsg[abs(ret)] return error('nl_send_simple() returned {0} ({1})'.format(ret, reason)) print('Sent {0} bytes to the kernel.'.format(ret)) # Finally we'll retrieve the kernel's answer, process it, and call any callbacks attached to the `nl_sock` instance. nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, None) # Add callback to the `nl_sock` instance. ret = nl_recvmsgs_default(sk) # Get kernel's answer, and call attached callbacks. if ret < 0: reason = errmsg[abs(ret)] return error('nl_recvmsgs_default() returned {0} ({1})'.format(ret, reason))
def test_two_attrs(): """Test with two attributes.""" msg = nlmsg_alloc() assert 0 == nla_put_u32(msg, 4, 8) nlh = nlmsg_hdr(msg) assert 24 == nlh.nlmsg_len assert 0 == nlh.nlmsg_type assert 0 == nlh.nlmsg_flags assert 0 == nlh.nlmsg_seq assert 0 == nlh.nlmsg_pid assert 0 == nla_put_u64(msg, 5, 17) assert 36 == nlh.nlmsg_len sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.s_local.nl_pid = 0 sk.s_seq_next = 0 nl_complete_msg(sk, msg) assert 0 == nlh.nlmsg_seq nlh.nlmsg_pid = 0 # sk.s_local.nl_pid is read-only in Python. expected = b'JAAAAAAABQAAAAAAAAAAAAgABAAIAAAADAAFABEAAAAAAAAA' assert expected == base64.b64encode(buffer(nlh.bytearray[:nlh.nlmsg_len]))
def test_full(tcp_server, monkeypatch): r"""Diff of C code (from test_bare()) to test against. --- test_bare.c 2015-02-08 12:43:15.543135855 -0800 +++ test_full.c 2015-02-08 12:43:08.533183752 -0800 @@ -13,10 +13,11 @@ struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; - sk->s_local.nl_pid = 0; - sk->s_seq_next = 0; + sk->s_local.nl_pid = 1234; + sk->s_seq_next = 10; - int ret = nl_send_simple(sk, RTM_GETLINK, 0, NULL, 0); + struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; + int ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); printf("Bytes: %d\n", ret); return 0; """ monkeypatch.setattr(libnl.socket_, 'generate_local_port', lambda: 1234) sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 1234 sk.s_seq_next = 10 rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert 1 == len(tcp_server.data) assert b'FAAAABIABQMKAAAA0gQAABEAAAA=' == base64.b64encode(buffer(tcp_server.data[0])) nl_socket_free(sk)
def test_nl_socket_modify_cb_error_verbose(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=verbose ./a.out #include <netlink/msg.h> static int callback(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { int *ret = arg; *ret = err->error; printf("Got something.\n"); return NL_STOP; } int main() { // Send data to the kernel. struct nl_sock *sk = nl_socket_alloc(); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", nl_connect(sk, NETLINK_ROUTE)); struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; int ret = nl_send_simple(sk, RTM_GETLINK, 0, &rt_hdr, sizeof(rt_hdr)); printf("Bytes Sent: %d\n", ret); // Retrieve kernel's response. int err = 0; nl_socket_modify_err_cb(sk, NL_CB_CUSTOM, callback, &err); printf("%d == err\n", err); printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); printf("%d == err\n", err); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0x124e0b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x124e0b8: Allocated new simple message // nlmsg_reserve: msg 0x124e0b8: Reserved 4 (1) bytes, pad=4, nlmsg_len=20 // nlmsg_append: msg 0x124e0b8: Appended 1 bytes with padding 4 // nl_sendmsg: sent 20 bytes // nlmsg_free: Returned message reference 0x124e0b8, 0 remaining // nlmsg_free: msg 0x124e0b8: Freed // Bytes Sent: 20 // 0 == err // recvmsgs: Attempting to read from 0x124e080 // recvmsgs: recvmsgs(0x124e080): Read 40 bytes // recvmsgs: recvmsgs(0x124e080): Processing valid message... // __nlmsg_alloc: msg 0x12520c0: Allocated new message, maxlen=40 // recvmsgs: recvmsgs(0x124e080): Increased expected sequence number to 1424136270 // Got something. // nlmsg_free: Returned message reference 0x12520c0, 0 remaining // nlmsg_free: msg 0x12520c0: Freed // -7 == nl_recvmsgs_default(sk) // -22 == err // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ got_something = list() def callback(_, err, arg): arg.append(err.error) return NL_STOP del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, 0, rt_hdr, rt_hdr.SIZEOF) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match('nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(1\) bytes, pad=4, nlmsg_len=20', log, True) assert match('nlmsg_append: msg 0x[a-f0-9]+: Appended 1 bytes with padding 4', log, True) assert match('nl_sendmsg: sent 20 bytes', log) assert not log assert 0 == nl_socket_modify_err_cb(sk, NL_CB_CUSTOM, callback, got_something) assert -7 == nl_recvmsgs_default(sk) assert [-22] == got_something assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 40 bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=40', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) nl_socket_free(sk) assert not log
def test_nl_socket_alloc(): 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_cb { nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1]; void * cb_args[NL_CB_TYPE_MAX+1]; nl_recvmsg_err_cb_t cb_err; void * cb_err_arg; int (*cb_recvmsgs_ow)(struct nl_sock *, struct nl_cb *); int (*cb_recv_ow)(struct nl_sock *, struct sockaddr_nl *, unsigned char **, struct ucred **); int (*cb_send_ow)(struct nl_sock *, struct nl_msg *); int cb_refcnt; enum nl_cb_type cb_active; }; 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; }; void print(struct nl_sock *sk) { printf("sk.s_local.nl_family = %d\n", sk->s_local.nl_family); printf("sk.s_local.nl_pid = %d # changes every process, remains same throughout proc.\n", sk->s_local.nl_pid); printf("sk.s_local.nl_groups = %d\n", sk->s_local.nl_groups); printf("sk.s_peer.nl_family = %d\n", sk->s_peer.nl_family); printf("sk.s_peer.nl_pid = %d\n", sk->s_peer.nl_pid); printf("sk.s_peer.nl_groups = %d\n", sk->s_peer.nl_groups); printf("sk.s_fd = %d\n", sk->s_fd); printf("sk.s_proto = %d\n", sk->s_proto); printf("sk.s_flags = %d\n", sk->s_flags); printf("sk.s_cb.cb_active = %d\n", sk->s_cb->cb_active); printf("addr: sk.s_cb.cb_err = %p\n", sk->s_cb->cb_err); } int main() { struct nl_sock *sk = nl_socket_alloc(); print(sk); nl_socket_free(sk); printf("\n"); struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); sk = nl_socket_alloc_cb(cb); nl_cb_put(cb); print(sk); nl_socket_free(sk); return 0; } // Expected output: // sk.s_local.nl_family = 16 // sk.s_local.nl_pid = 12309 # changes every process, remains same throughout proc. // sk.s_local.nl_groups = 0 // sk.s_peer.nl_family = 16 // sk.s_peer.nl_pid = 0 // sk.s_peer.nl_groups = 0 // sk.s_fd = -1 // sk.s_proto = 0 // sk.s_flags = 0 // sk.s_cb.cb_active = 11 // addr: sk.s_cb.cb_err = (nil) // // sk.s_local.nl_family = 16 // sk.s_local.nl_pid = 12309 # changes every process, remains same throughout proc. // sk.s_local.nl_groups = 0 // sk.s_peer.nl_family = 16 // sk.s_peer.nl_pid = 0 // sk.s_peer.nl_groups = 0 // sk.s_fd = -1 // sk.s_proto = 0 // sk.s_flags = 0 // sk.s_cb.cb_active = 11 // addr: sk.s_cb.cb_err = 0xb6f6eb40 """ sk = nl_socket_alloc() assert 16 == sk.s_local.nl_family assert 0 < sk.s_local.nl_pid assert 0 == sk.s_local.nl_groups assert 16 == sk.s_peer.nl_family assert 0 == sk.s_peer.nl_pid assert 0 == sk.s_peer.nl_groups assert -1 == sk.s_fd assert 0 == sk.s_proto assert 0 == sk.s_flags assert 11 == sk.s_cb.cb_active assert sk.s_cb.cb_err is None nl_socket_free(sk) first_pid = int(sk.s_local.nl_pid) sk = nl_socket_alloc(nl_cb_alloc(NL_CB_VERBOSE)) assert 16 == sk.s_local.nl_family assert first_pid == sk.s_local.nl_pid assert 0 == sk.s_local.nl_groups assert 16 == sk.s_peer.nl_family assert 0 == sk.s_peer.nl_pid assert 0 == sk.s_peer.nl_groups assert -1 == sk.s_fd assert 0 == sk.s_proto assert 0 == sk.s_flags assert 11 == sk.s_cb.cb_active assert sk.s_cb.cb_err is not None nl_socket_free(sk)
def test_nl_socket_modify_cb(log, ifaces): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 ./a.out #include <netlink/msg.h> static int callback(struct nl_msg *msg, void *arg) { printf("Got something.\n"); nl_msg_dump(msg, stdout); return NL_OK; } int main() { // Send data to the kernel. struct nl_sock *sk = nl_socket_alloc(); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", nl_connect(sk, NETLINK_ROUTE)); struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; int ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); printf("Bytes Sent: %d\n", ret); // Retrieve kernel's response. nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, NULL); printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0x1b840b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x1b840b8: Allocated new simple message // nlmsg_reserve: msg 0x1b840b8: Reserved 4 (1) bytes, pad=4, nlmsg_len=20 // nlmsg_append: msg 0x1b840b8: Appended 1 bytes with padding 4 // nl_sendmsg: sent 20 bytes // nlmsg_free: Returned message reference 0x1b840b8, 0 remaining // nlmsg_free: msg 0x1b840b8: Freed // Bytes Sent: 20 // recvmsgs: Attempting to read from 0x1b84080 // recvmsgs: recvmsgs(0x1b84080): Read 3364 bytes // recvmsgs: recvmsgs(0x1b84080): Processing valid message... // __nlmsg_alloc: msg 0x1b880c0: Allocated new message, maxlen=1116 // Got something. // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1116 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424133909 // .port = 6192 // [PAYLOAD] 1100 octets // 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I....... // 07 00 03 00 6c 6f 00 00 08 00 0d 00 00 00 00 00 ....lo.......... // <trimmed> // 00 00 00 00 00 00 00 00 00 00 00 00 ............ // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x1b84080): Processing valid message... // nlmsg_free: Returned message reference 0x1b880c0, 0 remaining // nlmsg_free: msg 0x1b880c0: Freed // __nlmsg_alloc: msg 0x1b880c0: Allocated new message, maxlen=1124 // Got something. // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1124 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424133909 // .port = 6192 // [PAYLOAD] 1108 octets // 00 00 01 00 02 00 00 00 43 10 01 00 00 00 00 00 ........C....... // 09 00 03 00 65 74 68 30 00 00 00 00 08 00 0d 00 ....eth0........ // <trimmed> // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x1b84080): Processing valid message... // nlmsg_free: Returned message reference 0x1b880c0, 0 remaining // nlmsg_free: msg 0x1b880c0: Freed // __nlmsg_alloc: msg 0x1b880c0: Allocated new message, maxlen=1124 // Got something. // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1124 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424133909 // .port = 6192 // [PAYLOAD] 1108 octets // 00 00 01 00 04 00 00 00 03 10 00 00 00 00 00 00 ................ // 0a 00 03 00 77 6c 61 6e 30 00 00 00 08 00 0d 00 ....wlan0....... // <trimmed> // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // nlmsg_free: Returned message reference 0x1b880c0, 0 remaining // nlmsg_free: msg 0x1b880c0: Freed // recvmsgs: Attempting to read from 0x1b84080 // recvmsgs: recvmsgs(0x1b84080): Read 20 bytes // recvmsgs: recvmsgs(0x1b84080): Processing valid message... // __nlmsg_alloc: msg 0x1b880c0: Allocated new message, maxlen=20 // recvmsgs: recvmsgs(0x1b84080): Increased expected sequence number to 1424133910 // nlmsg_free: Returned message reference 0x1b880c0, 0 remaining // nlmsg_free: msg 0x1b880c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ got_something = list() def callback(msg, arg): got_something.append(arg) nl_msg_dump(msg) return NL_OK del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match('nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(1\) bytes, pad=4, nlmsg_len=20', log, True) assert match('nlmsg_append: msg 0x[a-f0-9]+: Appended 1 bytes with padding 4', log, True) assert match('nl_sendmsg: sent 20 bytes', log) assert not log assert 0 == nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, 95) assert 0 == nl_recvmsgs_default(sk) assert [95] * len(ifaces) == got_something assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) for _ in ifaces: if 'Attempting to read' in log[0]: # Lots of network interfaces on this host. assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = \d{3,}', log, True) assert match('print_hdr: .type = 16 <0x10>', log) assert match('print_hdr: .flags = 2 <MULTI>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: \[PAYLOAD\] \d{3,} octets', log, True) rem = log.index('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------') assert 20 < rem # At least check that there were a lot of log statements skipped. log = log[rem:] assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 20 bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=20', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) nl_socket_free(sk) assert not log
def test_genl_ctrl_resolve(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=debug ./a.out #include <netlink/msg.h> int main() { struct nl_sock *sk = nl_socket_alloc(); printf("%d == genl_connect(sk)\n", genl_connect(sk)); int driver_id = genl_ctrl_resolve(sk, "nl80211"); printf("%d == driver_id\n", driver_id); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // 0 == genl_connect(sk) // nl_object_alloc: Allocated new object 0x6f90b8 // __nlmsg_alloc: msg 0x6f9110: Allocated new message, maxlen=4096 // nlmsg_put: msg 0x6f9110: Added netlink header type=16, flags=0, pid=0, seq=0 // nlmsg_reserve: msg 0x6f9110: Reserved 4 (4) bytes, pad=4, nlmsg_len=20 // genlmsg_put: msg 0x6f9110: Added generic netlink header cmd=3 version=1 // nla_reserve: msg 0x6f9110: attr <0x6f9164> 2: Reserved 12 (8) bytes at offset +4 nlmsg_len=32 // nla_put: msg 0x6f9110: attr <0x6f9164> 2: Wrote 8 bytes at offset +4 // -- Debug: Sent Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 32 // .type = 16 <genl/family::nlctrl> // .flags = 5 <REQUEST,ACK> // .seq = 1425769691 // .port = 2568 // [GENERIC NETLINK HEADER] 4 octets // .cmd = 3 // .version = 1 // .unused = 0 // [ATTR 02] 8 octets // 6e 6c 38 30 32 31 31 00 nl80211. // --------------------------- END NETLINK MESSAGE --------------------------- // nl_sendmsg: sent 32 bytes // recvmsgs: Attempting to read from 0x6f9080 // recvmsgs: recvmsgs(0x6f9080): Read 1836 bytes // recvmsgs: recvmsgs(0x6f9080): Processing valid message... // __nlmsg_alloc: msg 0x6fe1d8: Allocated new message, maxlen=1836 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1836 // .type = 16 <genl/family::nlctrl> // .flags = 0 <> // .seq = 1425769691 // .port = 2568 // [GENERIC NETLINK HEADER] 4 octets // .cmd = 1 // .version = 2 // .unused = 0 // [ATTR 02] 8 octets // 6e 6c 38 30 32 31 31 00 nl80211. // [ATTR 01] 2 octets // 16 00 .. // [PADDING] 2 octets // 00 00 .. // [ATTR 03] 4 octets // 01 00 00 00 .... // [ATTR 04] 4 octets // 00 00 00 00 .... // [ATTR 05] 4 octets // d5 00 00 00 .... // [ATTR 06] 1640 octets // 14 00 01 00 08 00 01 00 01 00 00 00 08 00 02 00 ................ // <trimmed> // 08 00 02 00 0b 00 00 00 ........ // [ATTR 07] 124 octets // 18 00 01 00 08 00 02 00 03 00 00 00 0b 00 01 00 ................ // 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config.......... // 04 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan.... // 1c 00 03 00 08 00 02 00 05 00 00 00 0f 00 01 00 ................ // 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory...... // 08 00 02 00 06 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme // 00 00 00 00 18 00 05 00 08 00 02 00 07 00 00 00 ................ // 0b 00 01 00 76 65 6e 64 6f 72 00 00 ....vendor.. // --------------------------- END NETLINK MESSAGE --------------------------- // nlmsg_free: Returned message reference 0x6fe1d8, 0 remaining // nlmsg_free: msg 0x6fe1d8: Freed // recvmsgs: Attempting to read from 0x6f9080 // recvmsgs: recvmsgs(0x6f9080): Read 36 bytes // recvmsgs: recvmsgs(0x6f9080): Processing valid message... // __nlmsg_alloc: msg 0x6fe1d8: Allocated new message, maxlen=36 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 36 // .type = 2 <ERROR> // .flags = 0 <> // .seq = 1425769691 // .port = 2568 // [ERRORMSG] 20 octets // .error = 0 "Success" // [ORIGINAL MESSAGE] 16 octets // __nlmsg_alloc: msg 0x6fe2b8: Allocated new message, maxlen=4096 // .nlmsg_len = 16 // .type = 16 <0x10> // .flags = 5 <REQUEST,ACK> // .seq = 1425769691 // .port = 2568 // nlmsg_free: Returned message reference 0x6fe2b8, 0 remaining // nlmsg_free: msg 0x6fe2b8: Freed // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x6f9080): Increased expected sequence number to 1425769692 // nlmsg_free: Returned message reference 0x6fe1d8, 0 remaining // nlmsg_free: msg 0x6fe1d8: Freed // nlmsg_free: Returned message reference 0x6f9110, 0 remaining // nlmsg_free: msg 0x6f9110: Freed // nl_object_put: Returned object reference 0x6f90b8, 0 remaining // nl_object_free: Freed object 0x6f90b8 // 22 == driver_id // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() assert 0 == genl_connect(sk) assert not log assert 20 <= genl_ctrl_resolve(sk, b'nl80211') nl_socket_free(sk) assert match('nl_object_alloc: Allocated new object 0x[a-f0-9]+', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match( 'nlmsg_put: msg 0x[a-f0-9]+: Added netlink header type=16, flags=0, pid=0, seq=0', log, True) assert match( 'nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(4\) bytes, pad=4, nlmsg_len=20', log, True) assert match( 'genlmsg_put: msg 0x[a-f0-9]+: Added generic netlink header cmd=3 version=1', log, True) assert match( 'nla_reserve: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Reserved 12 \(8\) bytes at offset \+4 nlmsg_len=32', log, True) assert match( 'nla_put: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Wrote 8 bytes at offset \+4', log, True) assert match('nl_msg_out_handler_debug: -- Debug: Sent Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 32', log) assert match('print_hdr: .type = 16 <genl/family::nlctrl>', log) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_genl_hdr: [GENERIC NETLINK HEADER] 4 octets', log) assert match('print_genl_hdr: .cmd = 3', log) assert match('print_genl_hdr: .version = 1', log) assert match('print_genl_hdr: .unused = 0', log) assert match('dump_attrs: [ATTR 02] 8 octets', log) assert match( 'dump_hex: 6e 6c 38 30 32 31 31 00 nl80211.', log) assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_sendmsg: sent 32 bytes', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{3,} bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = \d{3,}', log, True) assert match('print_hdr: .type = 16 <genl/family::nlctrl>', log) assert match('print_hdr: .flags = 0 <>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_genl_hdr: [GENERIC NETLINK HEADER] 4 octets', log) assert match('print_genl_hdr: .cmd = 1', log) assert match('print_genl_hdr: .version = 2', log) assert match('print_genl_hdr: .unused = 0', log) assert match('dump_attrs: [ATTR 02] 8 octets', log) assert match( 'dump_hex: 6e 6c 38 30 32 31 31 00 nl80211.', log) assert match('dump_attrs: [ATTR 01] 2 octets', log) assert match( 'dump_hex: .. 00 ..', log, True) assert match('dump_attrs: [PADDING] 2 octets', log) assert match( 'dump_hex: 00 00 ..', log) assert match('dump_attrs: [ATTR 03] 4 octets', log) assert match( 'dump_hex: 01 00 00 00 ....', log) assert match('dump_attrs: [ATTR 04] 4 octets', log) assert match( 'dump_hex: 00 00 00 00 ....', log) assert match('dump_attrs: [ATTR 05] 4 octets', log) assert match( 'dump_hex: .. 00 00 00 ....', log, True) assert match('dump_attrs: \[ATTR 06\] \d{4,} octets', log, True) assert match( 'dump_hex: 14 00 01 00 08 00 01 00 01 00 00 00 08 00 02 00 ................', log) # Done testing this payload. Too big. for line in log: if line.startswith('dump_hex'): continue rem = log.index(line) assert 20 < rem # At least check that there were a lot of log statements skipped. log = log[rem:] break assert match('dump_attrs: \[ATTR 07\] \d{3,} octets', log, True) assert match( 'dump_hex: 18 00 01 00 08 00 02 00 .. 00 00 00 0b 00 01 00 ................', log, True) assert match( 'dump_hex: 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config..........', log) assert match( 'dump_hex: .. 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan....', log, True) assert match( 'dump_hex: 1c 00 03 00 08 00 02 00 .. 00 00 00 0f 00 01 00 ................', log, True) assert match( 'dump_hex: 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory......', log) assert match( 'dump_hex: 08 00 02 00 .. 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme', log, True) rem = log.index( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------' ) log = log[rem:] assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 36 bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=36', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 36', log) assert match('print_hdr: .type = 2 <ERROR>', log) assert match('print_hdr: .flags = 0 <>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('dump_error_msg: [ERRORMSG] 20 octets', log) assert match('dump_error_msg: .error = 0 "Success"', log) assert match('dump_error_msg: [ORIGINAL MESSAGE] 16 octets', log) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('print_hdr: .nlmsg_len = 16', log) assert match('print_hdr: .type = 16 <0x[a-f0-9]+>', log, True) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{10}', log, True) assert not log
def main(): """Main function called upon script execution.""" # First get the wireless interface index. if OPTIONS['<interface>']: pack = struct.pack('16sI', OPTIONS['<interface>'].encode('ascii'), 0) sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: info = struct.unpack('16sI', fcntl.ioctl(sk.fileno(), 0x8933, pack)) except OSError: return error('Wireless interface {0} does not exist.'.format( OPTIONS['<interface>'])) finally: sk.close() if_index = int(info[1]) else: if_index = -1 # Then open a socket to the kernel. Same one used for sending and receiving. sk = nl_socket_alloc() # Creates an `nl_sock` instance. ret = genl_connect(sk) # Create file descriptor and bind socket. if ret < 0: reason = errmsg[abs(ret)] return error('genl_connect() returned {0} ({1})'.format(ret, reason)) # Now get the nl80211 driver ID. Handle errors here. driver_id = genl_ctrl_resolve(sk, b'nl80211') # Find the nl80211 driver ID. if driver_id < 0: reason = errmsg[abs(driver_id)] return error('genl_ctrl_resolve() returned {0} ({1})'.format( driver_id, reason)) # Setup the Generic Netlink message. msg = nlmsg_alloc() # Allocate a message. if OPTIONS['<interface>']: genlmsg_put(msg, 0, 0, driver_id, 0, 0, nl80211.NL80211_CMD_GET_INTERFACE, 0) # Tell kernel: send iface info. nla_put_u32(msg, nl80211.NL80211_ATTR_IFINDEX, if_index) # This is the interface we care about. else: # Ask kernel to send info for all wireless interfaces. genlmsg_put(msg, 0, 0, driver_id, 0, NLM_F_DUMP, nl80211.NL80211_CMD_GET_INTERFACE, 0) # Add the callback function to the nl_sock. has_printed = list() nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, has_printed) # Now send the message to the kernel, and get its response, automatically calling the callback. ret = nl_send_auto(sk, msg) if ret < 0: reason = errmsg[abs(ret)] return error('nl_send_auto() returned {0} ({1})'.format(ret, reason)) print('Sent {0} bytes to the kernel.'.format(ret)) ret = nl_recvmsgs_default( sk) # Blocks until the kernel replies. Usually it's instant. if ret < 0: reason = errmsg[abs(ret)] return error('nl_recvmsgs_default() returned {0} ({1})'.format( ret, reason))
def test_nl_cache_ops_associate_safe(): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && ./a.out #include <netlink/genl/family.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; }; struct nl_cache_ops { char *co_name; int co_hdrsize; int co_protocol; int co_hash_size; unsigned int co_flags; unsigned int co_refcnt; struct nl_af_group *co_groups; int (*co_request_update)(struct nl_cache*, struct nl_sock*); int (*co_msg_parser)(struct nl_cache_ops*, struct sockaddr_nl*, struct nlmsghdr*, struct nl_parser_param*); int (*co_event_filter)(struct nl_cache*, struct nl_object *obj); int (*co_include_event)(struct nl_cache *cache, struct nl_object *obj, change_func_t change_cb, void *data); void (*reserved_1)(void); void (*reserved_2)(void); void (*reserved_3)(void); void (*reserved_4)(void); void (*reserved_5)(void); void (*reserved_6)(void); void (*reserved_7)(void); void (*reserved_8)(void); struct nl_object_ops *co_obj_ops; }; struct nl_object_ops { char *oo_name; size_t oo_size; uint32_t oo_id_attrs; }; struct nl_msgtype { int mt_id; int mt_act; char *mt_name; }; static int callback(struct nl_sock *sk, struct nl_msg *msg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); printf("%d == nlh.nlmsg_len\n", nlh->nlmsg_len); printf("%d == nlh.nlmsg_type\n", nlh->nlmsg_type); printf("%d == nlh.nlmsg_flags\n", nlh->nlmsg_flags); struct nl_cache_ops *ops = nl_cache_ops_associate_safe(NETLINK_GENERIC, nlh->nlmsg_type); printf("'%s' == ops.co_name\n", ops->co_name); printf("%d == ops.co_hdrsize\n", ops->co_hdrsize); printf("%d == ops.co_protocol\n", ops->co_protocol); printf("%d == ops.co_hash_size\n", ops->co_hash_size); printf("%d == ops.co_flags\n", ops->co_flags); printf("'%s' == ops.co_obj_ops.oo_name\n", ops->co_obj_ops->oo_name); printf("%d == ops.co_obj_ops.oo_size\n", ops->co_obj_ops->oo_size); printf("%d == ops.co_obj_ops.oo_id_attrs\n", ops->co_obj_ops->oo_id_attrs); printf("%d == nlmsg_attrlen(nlh, ops.co_hdrsize)\n", nlmsg_attrlen(nlh, ops->co_hdrsize)); struct nl_msgtype *mt = nl_msgtype_lookup(ops, nlh->nlmsg_type); printf("%d == mt.mt_id\n", mt->mt_id); printf("%d == mt.mt_act\n", mt->mt_act); printf("'%s' == mt.mt_name\n", mt->mt_name); return NL_STOP; } int main() { struct nl_sock *sk = nl_socket_alloc(); nl_cb_overwrite_send(sk->s_cb, callback); struct genl_family *ret = genl_family_alloc(); genl_family_set_name(ret, "nl80211"); struct nl_msg *msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1); printf("%d == nl_send_auto(sk, msg)\n", nl_send_auto(sk, msg)); return 0; } // Expected output: // 20 == nlh.nlmsg_len // 16 == nlh.nlmsg_type // 5 == nlh.nlmsg_flags // 'genl/family' == ops.co_name // 4 == ops.co_hdrsize // 16 == ops.co_protocol // 0 == ops.co_hash_size // 0 == ops.co_flags // 'genl/family' == ops.co_obj_ops.oo_name // 80 == ops.co_obj_ops.oo_size // 1 == ops.co_obj_ops.oo_id_attrs // 0 == nlmsg_attrlen(nlh, ops.co_hdrsize) // 16 == mt.mt_id // 0 == mt.mt_act // 'nlctrl' == mt.mt_name // 2 == nl_send_auto(sk, msg) """ called = list() def callback(_, msg_): nlh = nlmsg_hdr(msg_) assert 20 == nlh.nlmsg_len assert 16 == nlh.nlmsg_type assert 5 == nlh.nlmsg_flags ops = nl_cache_ops_associate_safe(NETLINK_GENERIC, nlh.nlmsg_type) assert 'genl/family' == ops.co_name assert 4 == ops.co_hdrsize assert 16 == ops.co_protocol assert 0 == ops.co_hash_size assert 0 == ops.co_flags assert 'genl/family' == ops.co_obj_ops.oo_name assert 80 == ops.co_obj_ops.oo_size assert 1 == ops.co_obj_ops.oo_id_attrs assert 0 == nlmsg_attrlen(nlh, ops.co_hdrsize) mt = nl_msgtype_lookup(ops, nlh.nlmsg_type) assert 16 == mt.mt_id assert 0 == mt.mt_act assert 'nlctrl' == mt.mt_name called.append(True) return NL_STOP sk = nl_socket_alloc() nl_cb_overwrite_send(sk.s_cb, callback) ret = genl_family_alloc() genl_family_set_name(ret, 'nl80211') msg = nlmsg_alloc() genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1) assert 2 == nl_send_auto(sk, msg) assert [True] == called
def test_socket(tcp_server): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && (nc -l 2000 |base64 &) && sleep 0.1 && ./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() { struct nl_msg *msg = nlmsg_alloc(); struct nlmsghdr *nlh = nlmsg_hdr(msg); printf("%d == nlh->nlmsg_len\n", nlh->nlmsg_len); printf("%d == nlh->nlmsg_type\n", nlh->nlmsg_type); printf("%d == nlh->nlmsg_flags\n", nlh->nlmsg_flags); printf("%d == nlh->nlmsg_seq\n", nlh->nlmsg_seq); printf("%d == nlh->nlmsg_pid\n", nlh->nlmsg_pid); struct sockaddr_in sin = { .sin_port = htons(2000), .sin_family = AF_INET, }; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); int fd = socket(AF_INET, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &sin, sizeof(sin)); struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; sk->s_local.nl_pid = 0; sk->s_seq_next = 0; nl_complete_msg(sk, msg); printf("%d == nlh->nlmsg_seq\n", nlh->nlmsg_seq); struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len }; struct msghdr hdr = { .msg_iov = &iov, .msg_iovlen = 1, }; int ret = nl_sendmsg(sk, msg, &hdr); printf("Bytes: %d\n", ret); return 0; } // Expected bash output: // 16 == nlh->nlmsg_len // 0 == nlh->nlmsg_type // 0 == nlh->nlmsg_flags // 0 == nlh->nlmsg_seq // 0 == nlh->nlmsg_pid // 0 == nlh->nlmsg_seq // Bytes: 16 // EAAAAAAABQAAAAAAAAAAAA== """ msg = nlmsg_alloc() nlh = nlmsg_hdr(msg) assert 16 == nlh.nlmsg_len assert 0 == nlh.nlmsg_type assert 0 == nlh.nlmsg_flags assert 0 == nlh.nlmsg_seq assert 0 == nlh.nlmsg_pid sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 0 sk.s_seq_next = 0 nl_complete_msg(sk, msg) assert 0 == nlh.nlmsg_seq nlh.nlmsg_pid = 0 # sk.s_local.nl_pid is read-only in Python. iov = nlh.bytearray[:nlh.nlmsg_len] hdr = msghdr(msg_iov=iov) assert 16 == nl_sendmsg(sk, msg, hdr) assert 1 == len(tcp_server.data) assert b'EAAAAAAABQAAAAAAAAAAAA==' == base64.b64encode(buffer(iov)) assert b'EAAAAAAABQAAAAAAAAAAAA==' == base64.b64encode(buffer(tcp_server.data[0])) nl_socket_free(sk)
def test_multipart(log, ifaces): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=debug ./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. printf("Begin main()\n"); struct nl_sock *sk = nl_socket_alloc(); printf("Allocated socket.\n"); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", nl_connect(sk, NETLINK_ROUTE)); struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; int ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); printf("Bytes Sent: %d\n", ret); // Retrieve kernel's response. printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0x1bbe0b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x1bbe0b8: Allocated new simple message // nlmsg_reserve: msg 0x1bbe0b8: Reserved 4 (1) bytes, pad=4, nlmsg_len=20 // nlmsg_append: msg 0x1bbe0b8: Appended 1 bytes with padding 4 // -- Debug: Sent Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 20 // .type = 18 <0x12> // .flags = 773 <REQUEST,ACK,ROOT,MATCH> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 4 octets // 11 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // nl_sendmsg: sent 20 bytes // nlmsg_free: Returned message reference 0x1bbe0b8, 0 remaining // nlmsg_free: msg 0x1bbe0b8: Freed // Bytes Sent: 20 // recvmsgs: Attempting to read from 0x1bbe080 // recvmsgs: recvmsgs(0x1bbe080): Read 3364 bytes // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=1116 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1116 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 1100 octets // 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I....... // <trimmed> // 00 00 00 00 00 00 00 00 00 00 00 00 ............ // --------------------------- END NETLINK MESSAGE --------------------------- // -- Debug: Unhandled Valid message: type=0x10 length=1116 flags=<MULTI> sequence-nr=1424053819 pid=18409 // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=1124 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1124 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 1108 octets // 00 00 01 00 02 00 00 00 43 10 01 00 00 00 00 00 ........C....... // <trimmed> // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // -- Debug: Unhandled Valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424053819 pid=18409 // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=1124 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1124 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 1108 octets // 00 00 01 00 03 00 00 00 03 10 00 00 00 00 00 00 ................ // <trimmed> // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // -- Debug: Unhandled Valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424053819 pid=18409 // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // recvmsgs: Attempting to read from 0x1bbe080 // recvmsgs: recvmsgs(0x1bbe080): Read 20 bytes // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=20 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 20 // .type = 3 <DONE> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 4 octets // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x1bbe080): Increased expected sequence number to 1424053820 // -- Debug: End of multipart message block: type=DONE length=20 flags=<MULTI> sequence-nr=1424053819 pid=18409 // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match('nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(1\) bytes, pad=4, nlmsg_len=20', log, True) assert match('nlmsg_append: msg 0x[a-f0-9]+: Appended 1 bytes with padding 4', log, True) assert match('nl_msg_out_handler_debug: -- Debug: Sent Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 20', log) assert match('print_hdr: .type = 18 <0x12>', log) assert match('print_hdr: .flags = 773 <REQUEST,ACK,ROOT,MATCH>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: [PAYLOAD] 4 octets', log) assert match('dump_hex: 11 00 00 00 ....', log) assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_sendmsg: sent 20 bytes', log) assert not log assert 0 == nl_recvmsgs_default(sk) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = \d{3,}', log, True) assert match('print_hdr: .type = 16 <0x10>', log) assert match('print_hdr: .flags = 2 <MULTI>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: \[PAYLOAD\] \d{3,} octets', log, True) assert match('dump_hex: 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I.......', log) assert match('dump_hex: 07 00 03 00 6c 6f 00 00 08 00 0d 00 00 00 00 00 ....lo..........', log) assert match('dump_hex: 05 00 10 00 00 00 00 00 05 00 11 00 00 00 00 00 ................', log) for _ in ifaces: # Done testing this payload. Differs too much between Travis and Raspbian, and probably others. rem = log.index('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------') assert 20 < rem # At least check that there were a lot of log statements skipped. log = log[rem:] assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_valid_handler_debug: -- Debug: Unhandled Valid message: type=0x10 length=\d{3,} flags=<MULTI> ' 'sequence-nr=\d{10,} pid=\d{3,}', log, True) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 20 bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=20', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 20', log) assert match('print_hdr: .type = 3 <DONE>', log) assert match('print_hdr: .flags = 2 <MULTI>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: [PAYLOAD] 4 octets', log) assert match('dump_hex: 00 00 00 00 ....', log) assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) assert match('nl_finish_handler_debug: -- Debug: End of multipart message block: type=DONE length=20 flags=<MULTI> ' 'sequence-nr=\d{10,} pid=\d{3,}', log, True) nl_socket_free(sk) assert not log
def test_socket(tcp_server): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && (nc -l 2000 |base64 &) && sleep 0.1 && ./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() { struct nl_msg *msg = nlmsg_alloc(); printf("%d == nla_put_u32()\n", nla_put_u32(msg, 4, 8)); struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attr = nlmsg_find_attr(nlh, 0, 4); printf("%d == nla_type(attr)\n", nla_type(attr)); printf("%d == nla_get_u32(attr)\n", nla_get_u32(attr)); printf("%d == attr->nla_len\n", attr->nla_len); struct sockaddr_in sin = { .sin_port = htons(2000), .sin_family = AF_INET, }; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); int fd = socket(AF_INET, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &sin, sizeof(sin)); struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; sk->s_local.nl_pid = 0; nl_complete_msg(sk, msg); struct iovec iov = { .iov_base = attr, .iov_len = attr->nla_len }; struct msghdr hdr = { .msg_iov = &iov, .msg_iovlen = 1, }; int ret = nl_sendmsg(sk, msg, &hdr); printf("Bytes: %d\n", ret); // 14 return 0; } // Expected bash output: // 0 == nla_put_u32() // 4 == nla_type(attr) // 8 == nla_get_u32(attr) // 8 == attr->nla_len // Bytes: 8 // CAAEAAgAAAA= """ msg = nlmsg_alloc() assert 0 == libnl.attr.nla_put_u32(msg, 4, 8) nlh = nlmsg_hdr(msg) attr = nlmsg_find_attr(nlh, 0, 4) assert 4 == libnl.attr.nla_type(attr) assert 8 == libnl.attr.nla_get_u32(attr) assert 8 == attr.nla_len sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 0 nl_complete_msg(sk, msg) iov = attr.bytearray[: attr.nla_len] hdr = msghdr(msg_iov=iov) assert 8 == nl_sendmsg(sk, msg, hdr) assert 1 == len(tcp_server.data) assert b"CAAEAAgAAAA=" == base64.b64encode(buffer(iov)) assert b"CAAEAAgAAAA=" == base64.b64encode(buffer(tcp_server.data[0])) nl_socket_free(sk)
def test_socket(tcp_server): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && (nc -l 2000 |base64 &) && sleep 0.1 && ./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() { struct nl_msg *msg = nlmsg_alloc(); printf("%d == nla_put_u32()\n", nla_put_u32(msg, 4, 8)); struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attr = nlmsg_find_attr(nlh, 0, 4); printf("%d == nla_type(attr)\n", nla_type(attr)); printf("%d == nla_get_u32(attr)\n", nla_get_u32(attr)); printf("%d == attr->nla_len\n", attr->nla_len); struct sockaddr_in sin = { .sin_port = htons(2000), .sin_family = AF_INET, }; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); int fd = socket(AF_INET, SOCK_STREAM, 0); connect(fd, (struct sockaddr *) &sin, sizeof(sin)); struct nl_sock *sk = nl_socket_alloc(); nl_connect(sk, NETLINK_ROUTE); sk->s_fd = fd; sk->s_local.nl_pid = 0; nl_complete_msg(sk, msg); struct iovec iov = { .iov_base = attr, .iov_len = attr->nla_len }; struct msghdr hdr = { .msg_iov = &iov, .msg_iovlen = 1, }; int ret = nl_sendmsg(sk, msg, &hdr); printf("Bytes: %d\n", ret); // 14 return 0; } // Expected bash output: // 0 == nla_put_u32() // 4 == nla_type(attr) // 8 == nla_get_u32(attr) // 8 == attr->nla_len // Bytes: 8 // CAAEAAgAAAA= """ msg = nlmsg_alloc() assert 0 == libnl.attr.nla_put_u32(msg, 4, 8) nlh = nlmsg_hdr(msg) attr = nlmsg_find_attr(nlh, 0, 4) assert 4 == libnl.attr.nla_type(attr) assert 8 == libnl.attr.nla_get_u32(attr) assert 8 == attr.nla_len sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) sk.socket_instance.close() sk.socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.socket_instance.connect(tcp_server.server.server_address) sk.s_local.nl_pid = 0 nl_complete_msg(sk, msg) iov = attr.bytearray[:attr.nla_len] hdr = msghdr(msg_iov=iov) assert 8 == nl_sendmsg(sk, msg, hdr) assert 1 == len(tcp_server.data) assert b'CAAEAAgAAAA=' == base64.b64encode(buffer(iov)) assert b'CAAEAAgAAAA=' == base64.b64encode(buffer(tcp_server.data[0])) nl_socket_free(sk)
def test_error(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=debug ./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. printf("Begin main()\n"); struct nl_sock *sk = nl_socket_alloc(); printf("Allocated socket.\n"); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", 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. printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0x3df0b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x3df0b8: Allocated new simple message // -- Debug: Sent Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 16 // .type = 0 <0x0> // .flags = 5 <REQUEST,ACK> // .seq = 1423967746 // .port = 29930 // --------------------------- END NETLINK MESSAGE --------------------------- // nl_sendmsg: sent 16 bytes // nlmsg_free: Returned message reference 0x3df0b8, 0 remaining // nlmsg_free: msg 0x3df0b8: Freed // Bytes Sent: 16 // recvmsgs: Attempting to read from 0x3df080 // recvmsgs: recvmsgs(0x3df080): Read 36 bytes // recvmsgs: recvmsgs(0x3df080): Processing valid message... // __nlmsg_alloc: msg 0x3e30c0: Allocated new message, maxlen=36 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 36 // .type = 2 <ERROR> // .flags = 0 <> // .seq = 1423967746 // .port = 29930 // [ERRORMSG] 20 octets // .error = 0 "Success" // [ORIGINAL MESSAGE] 16 octets // __nlmsg_alloc: msg 0x3e3128: Allocated new message, maxlen=4096 // .nlmsg_len = 16 // .type = 0 <0x0> // .flags = 5 <REQUEST,ACK> // .seq = 1423967746 // .port = 29930 // nlmsg_free: Returned message reference 0x3e3128, 0 remaining // nlmsg_free: msg 0x3e3128: Freed // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x3df080): Increased expected sequence number to 1423967746 // -- Debug: ACK: type=ERROR length=36 flags=<> sequence-nr=1423967746 pid=29930 // nlmsg_free: Returned message reference 0x3e30c0, 0 remaining // nlmsg_free: msg 0x3e30c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) assert 16 == nl_send_simple(sk, 0, NLM_F_REQUEST, None) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match('nl_msg_out_handler_debug: -- Debug: Sent Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 16', log) assert match('print_hdr: .type = 0 <0x0>', log) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_sendmsg: sent 16 bytes', log) assert not log assert 0 == nl_recvmsgs_default(sk) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 36 bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=36', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 36', log) assert match('print_hdr: .type = 2 <ERROR>', log) assert match('print_hdr: .flags = 0 <>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('dump_error_msg: [ERRORMSG] 20 octets', log) assert match('dump_error_msg: .error = 0 "Success"', log) assert match('dump_error_msg: [ORIGINAL MESSAGE] 16 octets', log) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('print_hdr: .nlmsg_len = 16', log) assert match('print_hdr: .type = 0 <0x0>', log) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) assert match('nl_ack_handler_debug: -- Debug: ACK: type=ERROR length=36 flags=<> sequence-nr=\d{10,} pid=\d{3,}', log, True) nl_socket_free(sk) assert not log
def test_multipart_verbose(log, ifaces): """Expected output (trimmed). // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0xa180b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0xa180b8: Allocated new simple message // nlmsg_reserve: msg 0xa180b8: Reserved 4 (1) bytes, pad=4, nlmsg_len=20 // nlmsg_append: msg 0xa180b8: Appended 1 bytes with padding 4 // nl_sendmsg: sent 20 bytes // nlmsg_free: Returned message reference 0xa180b8, 0 remaining // nlmsg_free: msg 0xa180b8: Freed // Bytes Sent: 20 // recvmsgs: Attempting to read from 0xa18080 // recvmsgs: recvmsgs(0xa18080): Read 3364 bytes // recvmsgs: recvmsgs(0xa18080): Processing valid message... // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=1116 // -- Warning: unhandled valid message: type=0x10 length=1116 flags=<MULTI> sequence-nr=1424132449 pid=5810 // recvmsgs: recvmsgs(0xa18080): Processing valid message... // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=1124 // -- Warning: unhandled valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424132449 pid=5810 // recvmsgs: recvmsgs(0xa18080): Processing valid message... // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=1124 // -- Warning: unhandled valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424132449 pid=5810 // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // recvmsgs: Attempting to read from 0xa18080 // recvmsgs: recvmsgs(0xa18080): Read 20 bytes // recvmsgs: recvmsgs(0xa18080): Processing valid message... // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=20 // recvmsgs: recvmsgs(0xa18080): Increased expected sequence number to 1424132450 // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match('nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(1\) bytes, pad=4, nlmsg_len=20', log, True) assert match('nlmsg_append: msg 0x[a-f0-9]+: Appended 1 bytes with padding 4', log, True) assert match('nl_sendmsg: sent 20 bytes', log) assert not log assert 0 == nl_recvmsgs_default(sk) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) for _ in ifaces: if 'Attempting to read' in log[0]: # Lots of network interfaces on this host. assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('nl_valid_handler_verbose: -- Warning: unhandled valid message: type=0x10 length=\d{3,} ' 'flags=<MULTI> sequence-nr=\d{10,} pid=\d{3,}', log, True) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 20 bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=20', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) nl_socket_free(sk) assert not log
def test_ctrl_cmd_getfamily_hex_dump(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 ./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; }; static void prefix_line(FILE *ofd, int prefix) { int i; for (i = 0; i < prefix; i++) fprintf(ofd, " "); } static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) { int i, a, c, limit; char ascii[21] = {0}; limit = 16 - (prefix * 2); prefix_line(ofd, prefix); fprintf(ofd, " "); for (i = 0, a = 0, c = 0; i < len; i++) { int v = *(uint8_t *) (start + i); fprintf(ofd, "%02x ", v); ascii[a++] = isprint(v) ? v : '.'; if (++c >= limit) { fprintf(ofd, "%s\n", ascii); if (i < (len - 1)) { prefix_line(ofd, prefix); fprintf(ofd, " "); } a = c = 0; memset(ascii, 0, sizeof(ascii)); } } if (c != 0) { for (i = 0; i < (limit - c); i++) fprintf(ofd, " "); fprintf(ofd, "%s\n", ascii); } } struct ucred { pid_t pid; uid_t uid; gid_t gid; }; struct nl_msg { int nm_protocol; int nm_flags; struct sockaddr_nl nm_src; struct sockaddr_nl nm_dst; struct ucred nm_creds; struct nlmsghdr *nm_nlh; size_t nm_size; int nm_refcnt; }; static int callback_send(struct nl_sock *sk, struct nl_msg *msg) { printf("%d == msg.nm_protocol\n", msg->nm_protocol); printf("%d == msg.nm_flags\n", msg->nm_flags); printf("%d == msg.nm_src.nl_family\n", msg->nm_src.nl_family); printf("%d == msg.nm_src.nl_pid\n", msg->nm_src.nl_pid); printf("%d == msg.nm_src.nl_groups\n", msg->nm_src.nl_groups); printf("%d == msg.nm_dst.nl_family\n", msg->nm_dst.nl_family); printf("%d == msg.nm_dst.nl_pid\n", msg->nm_dst.nl_pid); printf("%d == msg.nm_dst.nl_groups\n", msg->nm_dst.nl_groups); printf("%d == msg.nm_creds.pid\n", msg->nm_creds.pid); printf("%d == msg.nm_creds.uid\n", msg->nm_creds.uid); printf("%d == msg.nm_creds.gid\n", msg->nm_creds.gid); printf("%d == msg.nm_nlh.nlmsg_type\n", msg->nm_nlh->nlmsg_type); printf("%d == msg.nm_nlh.nlmsg_flags\n", msg->nm_nlh->nlmsg_flags); printf("%d == msg.nm_nlh.nlmsg_pid\n", msg->nm_nlh->nlmsg_pid); printf("%d == msg.nm_size\n", msg->nm_size); printf("%d == msg.nm_refcnt\n", msg->nm_refcnt); struct iovec iov = { .iov_base = (void *) nlmsg_hdr(msg), .iov_len = nlmsg_hdr(msg)->nlmsg_len, }; dump_hex(stdout, iov.iov_base, iov.iov_len, 0); return nl_send_iovec(sk, msg, &iov, 1); } static int callback_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) { int n = nl_recv(sk, nla, buf, creds); dump_hex(stdout, (void *) *buf, n, 0); return n; } static int callback_recv_msg(struct nl_msg *msg, void *arg) { printf("%d == msg.nm_protocol\n", msg->nm_protocol); printf("%d == msg.nm_flags\n", msg->nm_flags); printf("%d == msg.nm_src.nl_family\n", msg->nm_src.nl_family); printf("%d == msg.nm_src.nl_pid\n", msg->nm_src.nl_pid); printf("%d == msg.nm_src.nl_groups\n", msg->nm_src.nl_groups); printf("%d == msg.nm_dst.nl_family\n", msg->nm_dst.nl_family); printf("%d == msg.nm_dst.nl_pid\n", msg->nm_dst.nl_pid); printf("%d == msg.nm_dst.nl_groups\n", msg->nm_dst.nl_groups); printf("%d == msg.nm_creds.pid\n", msg->nm_creds.pid); printf("%d == msg.nm_creds.uid\n", msg->nm_creds.uid); printf("%d == msg.nm_creds.gid\n", msg->nm_creds.gid); printf("%d == msg.nm_nlh.nlmsg_type\n", msg->nm_nlh->nlmsg_type); printf("%d == msg.nm_nlh.nlmsg_flags\n", msg->nm_nlh->nlmsg_flags); printf("%d == msg.nm_nlh.nlmsg_pid\n", msg->nm_nlh->nlmsg_pid); printf("%d == msg.nm_size\n", msg->nm_size); printf("%d == msg.nm_refcnt\n", msg->nm_refcnt); dump_hex(stdout, (char *) msg->nm_nlh, nlmsg_datalen(msg->nm_nlh), 0); return NL_OK; } int main() { struct nl_sock *sk = nl_socket_alloc(); nl_cb_overwrite_send(sk->s_cb, callback_send); nl_cb_overwrite_recv(sk->s_cb, callback_recv); printf("%d == genl_connect(sk)\n", genl_connect(sk)); struct genl_family *ret = (struct genl_family *) genl_family_alloc(); genl_family_set_name(ret, "nl80211"); struct nl_msg *msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1); nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, "nl80211"); nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback_recv_msg, NULL); printf("%d == nl_send_auto(sk, msg)\n", nl_send_auto(sk, msg)); printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // 0 == genl_connect(sk) // nl_object_alloc: Allocated new object 0x2b50b8 // __nlmsg_alloc: msg 0x2b5110: Allocated new message, maxlen=4096 // nlmsg_put: msg 0x2b5110: Added netlink header type=16, flags=0, pid=0, seq=0 // nlmsg_reserve: msg 0x2b5110: Reserved 4 (4) bytes, pad=4, nlmsg_len=20 // genlmsg_put: msg 0x2b5110: Added generic netlink header cmd=3 version=1 // nla_reserve: msg 0x2b5110: attr <0x2b5164> 2: Reserved 12 (8) bytes at offset +4 nlmsg_len=32 // nla_put: msg 0x2b5110: attr <0x2b5164> 2: Wrote 8 bytes at offset +4 // 16 == msg.nm_protocol // 0 == msg.nm_flags // 0 == msg.nm_src.nl_family // 0 == msg.nm_src.nl_pid // 0 == msg.nm_src.nl_groups // 0 == msg.nm_dst.nl_family // 0 == msg.nm_dst.nl_pid // 0 == msg.nm_dst.nl_groups // 0 == msg.nm_creds.pid // 0 == msg.nm_creds.uid // 0 == msg.nm_creds.gid // 16 == msg.nm_nlh.nlmsg_type // 5 == msg.nm_nlh.nlmsg_flags // 14272 == msg.nm_nlh.nlmsg_pid // 4096 == msg.nm_size // 1 == msg.nm_refcnt // 20 00 00 00 10 00 05 00 af aa f6 54 c0 37 00 00 ..........T.7.. // 03 01 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211. // nl_sendmsg: sent 32 bytes // 32 == nl_send_auto(sk, msg) // recvmsgs: Attempting to read from 0x2b5080 // 2c 07 00 00 10 00 00 00 af aa f6 54 c0 37 00 00 ,..........T.7.. // 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211. // 06 00 01 00 16 00 00 00 08 00 03 00 01 00 00 00 ................ // 08 00 04 00 00 00 00 00 08 00 05 00 d5 00 00 00 ................ // 6c 06 06 00 14 00 01 00 08 00 01 00 01 00 00 00 l............... // 08 00 02 00 0e 00 00 00 14 00 02 00 08 00 01 00 ................ // <trimmed> // 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config.......... // 04 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan.... // 1c 00 03 00 08 00 02 00 05 00 00 00 0f 00 01 00 ................ // 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory...... // 08 00 02 00 06 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme // 00 00 00 00 18 00 05 00 08 00 02 00 07 00 00 00 ................ // 0b 00 01 00 76 65 6e 64 6f 72 00 00 ....vendor.. // recvmsgs: recvmsgs(0x2b5080): Read 1836 bytes // recvmsgs: recvmsgs(0x2b5080): Processing valid message... // __nlmsg_alloc: msg 0x2ba160: Allocated new message, maxlen=1836 // 16 == msg.nm_protocol // 0 == msg.nm_flags // 16 == msg.nm_src.nl_family // 0 == msg.nm_src.nl_pid // 0 == msg.nm_src.nl_groups // 0 == msg.nm_dst.nl_family // 0 == msg.nm_dst.nl_pid // 0 == msg.nm_dst.nl_groups // 0 == msg.nm_creds.pid // 0 == msg.nm_creds.uid // 0 == msg.nm_creds.gid // 16 == msg.nm_nlh.nlmsg_type // 0 == msg.nm_nlh.nlmsg_flags // 14272 == msg.nm_nlh.nlmsg_pid // 1836 == msg.nm_size // 1 == msg.nm_refcnt // 2c 07 00 00 10 00 00 00 af aa f6 54 c0 37 00 00 ,..........T.7.. // 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211. // 06 00 01 00 16 00 00 00 08 00 03 00 01 00 00 00 ................ // 08 00 04 00 00 00 00 00 08 00 05 00 d5 00 00 00 ................ // 6c 06 06 00 14 00 01 00 08 00 01 00 01 00 00 00 l............... // 08 00 02 00 0e 00 00 00 14 00 02 00 08 00 01 00 ................ // <trimmed> // 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config.......... // 04 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan.... // 1c 00 03 00 08 00 02 00 05 00 00 00 0f 00 01 00 ................ // 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory...... // 08 00 02 00 06 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme // 00 00 00 00 18 00 05 00 08 00 02 00 ............ // nlmsg_free: Returned message reference 0x2ba160, 0 remaining // nlmsg_free: msg 0x2ba160: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ def callback_send(sk, msg): assert 16 == msg.nm_protocol assert 0 == msg.nm_flags assert 0 == msg.nm_src.nl_family assert 0 == msg.nm_src.nl_pid assert 0 == msg.nm_src.nl_groups assert 0 == msg.nm_dst.nl_family assert 0 == msg.nm_dst.nl_pid assert 0 == msg.nm_dst.nl_groups assert msg.nm_creds is None assert 16 == msg.nm_nlh.nlmsg_type assert 5 == msg.nm_nlh.nlmsg_flags assert 100 < msg.nm_nlh.nlmsg_pid assert 1 == msg.nm_refcnt hdr = nlmsg_hdr(msg) iov = hdr.bytearray[:hdr.nlmsg_len] dump_hex(logging.getLogger().debug, iov, len(iov), 0) return nl_send_iovec(sk, msg, iov, 1) def callback_recv(sk, nla, buf, creds): n = nl_recv(sk, nla, buf, creds) dump_hex(logging.getLogger().debug, buf, len(buf), 0) return n def callback_recv_msg(msg, _): assert 16 == msg.nm_protocol assert 0 == msg.nm_flags assert 16 == msg.nm_src.nl_family assert 0 == msg.nm_src.nl_pid assert 0 == msg.nm_src.nl_groups assert 0 == msg.nm_dst.nl_family assert 0 == msg.nm_dst.nl_pid assert 0 == msg.nm_dst.nl_groups assert msg.nm_creds is None assert 16 == msg.nm_nlh.nlmsg_type assert 0 == msg.nm_nlh.nlmsg_flags assert 100 < msg.nm_nlh.nlmsg_pid assert 1000 < msg.nm_size assert 1 == msg.nm_refcnt dump_hex(logging.getLogger().debug, msg.nm_nlh.bytearray, nlmsg_datalen(msg.nm_nlh), 0) return NL_OK del log[:] sk_main = nl_socket_alloc() nl_cb_overwrite_send(sk_main.s_cb, callback_send) nl_cb_overwrite_recv(sk_main.s_cb, callback_recv) genl_connect(sk_main) ret = genl_family_alloc() genl_family_set_name(ret, b'nl80211') msg_main = nlmsg_alloc() genlmsg_put(msg_main, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1) nla_put_string(msg_main, CTRL_ATTR_FAMILY_NAME, b'nl80211') nl_socket_modify_cb(sk_main, NL_CB_VALID, NL_CB_CUSTOM, callback_recv_msg, None) assert 32 == nl_send_auto(sk_main, msg_main) assert 0 == nl_recvmsgs_default(sk_main) nl_socket_free(sk_main) assert match('nl_object_alloc: Allocated new object 0x[a-f0-9]+', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_put: msg 0x[a-f0-9]+: Added netlink header type=16, flags=0, pid=0, seq=0', log, True) assert match('nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(4\) bytes, pad=4, nlmsg_len=20', log, True) assert match('genlmsg_put: msg 0x[a-f0-9]+: Added generic netlink header cmd=3 version=1', log, True) assert match( 'nla_reserve: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Reserved 12 \(8\) bytes at offset \+4 nlmsg_len=32', log, True) assert match('nla_put: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Wrote 8 bytes at offset \+4', log, True) assert match('dump_hex: 20 00 00 00 10 00 05 00 .. .. .. .. .. .. 00 00 ...............', log, True) assert match('dump_hex: 03 01 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211.', log) assert match('nl_sendmsg: sent 32 bytes', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('dump_hex: .. .. 00 00 10 00 00 00 .. .. .. .. .. .. 00 00 ................', log, True) assert match('dump_hex: 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211.', log) assert match('dump_hex: 06 00 01 00 .. 00 00 00 08 00 03 00 01 00 00 00 ................', log, True) assert match('dump_hex: 08 00 04 00 00 00 00 00 08 00 05 00 .. 00 00 00 ................', log, True) for i in range(len(log)): if re.match(r'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log[i]): log = log[i:] break assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{3,} bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('dump_hex: .. .. 00 00 10 00 00 00 .. .. .. .. .. .. 00 00 ................', log, True) assert match('dump_hex: 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211.', log) assert match('dump_hex: 06 00 01 00 .. 00 00 00 08 00 03 00 01 00 00 00 ................', log, True) assert match('dump_hex: 08 00 04 00 00 00 00 00 08 00 05 00 .. 00 00 00 ................', log, True) while log and log[0].startswith('dump_hex:'): log.pop(0) assert not log
def test_defaults(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 ./a.out #include <netlink/msg.h> struct ucred { __u32 pid; __u32 uid; __u32 gid; }; struct nl_msg { int nm_protocol; int nm_flags; struct sockaddr_nl nm_src; struct sockaddr_nl nm_dst; struct ucred nm_creds; struct nlmsghdr *nm_nlh; size_t nm_size; int nm_refcnt; }; 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; }; void print(struct nl_msg *msg) { printf("%d == msg.nm_protocol\n", msg->nm_protocol); printf("%d == msg.nm_flags\n", msg->nm_flags); printf("%d == msg.nm_src.nl_family\n", msg->nm_src.nl_family); printf("%d == msg.nm_src.nl_pid\n", msg->nm_src.nl_pid); printf("%d == msg.nm_src.nl_groups\n", msg->nm_src.nl_groups); printf("%d == msg.nm_dst.nl_family\n", msg->nm_dst.nl_family); printf("%d == msg.nm_dst.nl_pid\n", msg->nm_dst.nl_pid); printf("%d == msg.nm_dst.nl_groups\n", msg->nm_dst.nl_groups); printf("%d == msg.nm_nlh.nlmsg_type\n", msg->nm_nlh->nlmsg_type); printf("%d == msg.nm_nlh.nlmsg_flags\n", msg->nm_nlh->nlmsg_flags); printf("%d == msg.nm_nlh.nlmsg_pid\n", msg->nm_nlh->nlmsg_pid); } int main() { printf("Begin main()\n"); struct nl_sock *sk = nl_socket_alloc(); printf("Allocated socket.\n"); struct nl_msg *msg = nlmsg_alloc_simple(0, 0); printf("Allocated message.\n"); printf("%d == nl_socket_get_local_port(sk)\n", nl_socket_get_local_port(sk)); printf("%d == sk.s_proto\n", sk->s_proto); printf("\n"); print(msg); printf("\n"); nl_complete_msg(sk, msg); print(msg); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // __nlmsg_alloc: msg 0x1e9c0b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x1e9c0b8: Allocated new simple message // Allocated message. // 10083 == nl_socket_get_local_port(sk) // 0 == sk.s_proto // // -1 == msg.nm_protocol // 0 == msg.nm_flags // 0 == msg.nm_src.nl_family // 0 == msg.nm_src.nl_pid // 0 == msg.nm_src.nl_groups // 0 == msg.nm_dst.nl_family // 0 == msg.nm_dst.nl_pid // 0 == msg.nm_dst.nl_groups // 0 == msg.nm_nlh.nlmsg_type // 0 == msg.nm_nlh.nlmsg_flags // 0 == msg.nm_nlh.nlmsg_pid // // 0 == msg.nm_protocol // 0 == msg.nm_flags // 0 == msg.nm_src.nl_family // 0 == msg.nm_src.nl_pid // 0 == msg.nm_src.nl_groups // 0 == msg.nm_dst.nl_family // 0 == msg.nm_dst.nl_pid // 0 == msg.nm_dst.nl_groups // 0 == msg.nm_nlh.nlmsg_type // 5 == msg.nm_nlh.nlmsg_flags // 10083 == msg.nm_nlh.nlmsg_pid // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() msg = nlmsg_alloc_simple(0, 0) assert re.match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message', log.pop(0)) assert re.match('nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log.pop(0)) local_port = int(nl_socket_get_local_port(sk)) proto = int(sk.s_proto) assert 0 < local_port assert 0 == proto assert -1 == msg.nm_protocol assert 0 == msg.nm_flags assert 0 == msg.nm_src.nl_family assert 0 == msg.nm_src.nl_pid assert 0 == msg.nm_src.nl_groups assert 0 == msg.nm_dst.nl_family assert 0 == msg.nm_dst.nl_pid assert 0 == msg.nm_dst.nl_groups assert 0 == msg.nm_nlh.nlmsg_type assert 0 == msg.nm_nlh.nlmsg_flags assert 0 == msg.nm_nlh.nlmsg_pid nl_complete_msg(sk, msg) assert proto == msg.nm_protocol assert 0 == msg.nm_flags assert 0 == msg.nm_src.nl_family assert 0 == msg.nm_src.nl_pid assert 0 == msg.nm_src.nl_groups assert 0 == msg.nm_dst.nl_family assert 0 == msg.nm_dst.nl_pid assert 0 == msg.nm_dst.nl_groups assert 0 == msg.nm_nlh.nlmsg_type assert 5 == msg.nm_nlh.nlmsg_flags assert local_port == msg.nm_nlh.nlmsg_pid assert not log nl_socket_free(sk)
def test_nl_connect(): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && ./a.out #include <netlink/msg.h> #include <dirent.h> struct nl_cb { nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1]; void * cb_args[NL_CB_TYPE_MAX+1]; nl_recvmsg_err_cb_t cb_err; void * cb_err_arg; int (*cb_recvmsgs_ow)(struct nl_sock *, struct nl_cb *); int (*cb_recv_ow)(struct nl_sock *, struct sockaddr_nl *, unsigned char **, struct ucred **); int (*cb_send_ow)(struct nl_sock *, struct nl_msg *); int cb_refcnt; enum nl_cb_type cb_active; }; 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; }; void print_fd_count() { int fd_count = 0; char buf[64]; struct dirent *dp; snprintf(buf, 256, "/proc/self/fd/"); DIR *dir = opendir(buf); while ((dp = readdir(dir)) != NULL) fd_count++; closedir(dir); printf("fd_count: %d\n", fd_count); } void print(struct nl_sock *sk) { printf("sk.s_local.nl_family = %d\n", sk->s_local.nl_family); printf("sk.s_local.nl_pid = %d # changes every process, remains same throughout proc.\n", sk->s_local.nl_pid); printf("sk.s_local.nl_groups = %d\n", sk->s_local.nl_groups); printf("sk.s_peer.nl_family = %d\n", sk->s_peer.nl_family); printf("sk.s_peer.nl_pid = %d\n", sk->s_peer.nl_pid); printf("sk.s_peer.nl_groups = %d\n", sk->s_peer.nl_groups); printf("sk.s_fd = %d\n", sk->s_fd); printf("sk.s_proto = %d\n", sk->s_proto); printf("sk.s_flags = %d\n", sk->s_flags); printf("sk.s_cb.cb_active = %d\n", sk->s_cb->cb_active); printf("addr: sk.s_cb.cb_err = %p\n", sk->s_cb->cb_err); } int main() { struct nl_sock *sk = nl_socket_alloc(); print_fd_count(); print(sk); printf("\n"); printf("nl_connect(): %d\n", nl_connect(sk, NETLINK_ROUTE)); print_fd_count(); print(sk); nl_socket_free(sk); print_fd_count(); print(sk); printf("\n"); sk = nl_socket_alloc(); printf("nl_connect(): %d\n", nl_connect(sk, NETLINK_GENERIC)); print_fd_count(); print(sk); nl_socket_free(sk); print_fd_count(); printf("\n"); return 0; } // Expected output: // fd_count: 6 // sk.s_local.nl_family = 16 // sk.s_local.nl_pid = 3121 # changes every process, remains same throughout proc. // sk.s_local.nl_groups = 0 // sk.s_peer.nl_family = 16 // sk.s_peer.nl_pid = 0 // sk.s_peer.nl_groups = 0 // sk.s_fd = -1 // sk.s_proto = 0 // sk.s_flags = 0 // sk.s_cb.cb_active = 11 // addr: sk.s_cb.cb_err = (nil) // // nl_connect(): 0 // fd_count: 7 // sk.s_local.nl_family = 16 // sk.s_local.nl_pid = 3121 # changes every process, remains same throughout proc. // sk.s_local.nl_groups = 0 // sk.s_peer.nl_family = 16 // sk.s_peer.nl_pid = 0 // sk.s_peer.nl_groups = 0 // sk.s_fd = 3 // sk.s_proto = 0 // sk.s_flags = 1 // sk.s_cb.cb_active = 11 // addr: sk.s_cb.cb_err = (nil) // fd_count: 6 // sk.s_local.nl_family = 16 // sk.s_local.nl_pid = 167775232 # changes every process, remains same throughout proc. // sk.s_local.nl_groups = 0 // sk.s_peer.nl_family = 16 // sk.s_peer.nl_pid = 0 // sk.s_peer.nl_groups = 0 // sk.s_fd = 3 // sk.s_proto = 0 // sk.s_flags = 1 // sk.s_cb.cb_active = 18774 // addr: sk.s_cb.cb_err = 0x310a0010 // // nl_connect(): 0 // fd_count: 7 // sk.s_local.nl_family = 16 // sk.s_local.nl_pid = 3121 # changes every process, remains same throughout proc. // sk.s_local.nl_groups = 0 // sk.s_peer.nl_family = 16 // sk.s_peer.nl_pid = 0 // sk.s_peer.nl_groups = 0 // sk.s_fd = 3 // sk.s_proto = 16 // sk.s_flags = 1 // sk.s_cb.cb_active = 11 // addr: sk.s_cb.cb_err = (nil) // fd_count: 6 """ initial_fd_count = len(os.listdir('/proc/self/fd')) assert 2 <= initial_fd_count # Allocate but don't connect/bind. sk = nl_socket_alloc() assert initial_fd_count == len(os.listdir('/proc/self/fd')) assert 16 == sk.s_local.nl_family assert 0 < sk.s_local.nl_pid assert 0 == sk.s_local.nl_groups assert 16 == sk.s_peer.nl_family assert 0 == sk.s_peer.nl_pid assert 0 == sk.s_peer.nl_groups assert -1 == sk.s_fd assert 0 == sk.s_proto assert 0 == sk.s_flags assert 11 == sk.s_cb.cb_active assert sk.s_cb.cb_err is None persistent_pid = int(sk.s_local.nl_pid) # Connect, then close the socket at the end. assert 0 == nl_connect(sk, NETLINK_ROUTE) assert initial_fd_count + 1 == len(os.listdir('/proc/self/fd')) assert 16 == sk.s_local.nl_family assert persistent_pid == sk.s_local.nl_pid assert 0 == sk.s_local.nl_groups assert 16 == sk.s_peer.nl_family assert 0 == sk.s_peer.nl_pid assert 0 == sk.s_peer.nl_groups assert 0 < sk.s_fd assert 0 == sk.s_proto assert 1 == sk.s_flags assert 11 == sk.s_cb.cb_active assert sk.s_cb.cb_err is None persistent_fd = int(sk.s_fd) nl_socket_free(sk) assert initial_fd_count == len(os.listdir('/proc/self/fd')) assert 16 == sk.s_local.nl_family # assert persistent_pid == sk.s_local.nl_pid # In C, pointer points to deallocated memory. In Python, leave alone. assert 0 == sk.s_local.nl_groups assert 16 == sk.s_peer.nl_family assert 0 == sk.s_peer.nl_pid assert 0 == sk.s_peer.nl_groups # assert persistent_fd == sk.s_fd # In C, s_fd is a regular int. In Python, it's a class property. assert 0 == sk.s_proto assert 1 == sk.s_flags # assert 11 == sk.s_cb.cb_active # In C, pointer points to deallocated memory. In Python, leave alone. # assert sk.s_cb.cb_err is None # In C, pointer points to deallocated memory. In Python, leave alone. # Re-allocate and connect again, pid should be the same as the previous session. sk = nl_socket_alloc() assert 0 == nl_connect(sk, NETLINK_GENERIC) assert initial_fd_count + 1 == len(os.listdir('/proc/self/fd')) assert 16 == sk.s_local.nl_family assert persistent_pid == sk.s_local.nl_pid assert 0 == sk.s_local.nl_groups assert 16 == sk.s_peer.nl_family assert 0 == sk.s_peer.nl_pid assert 0 == sk.s_peer.nl_groups assert persistent_fd == sk.s_fd assert 16 == sk.s_proto assert 1 == sk.s_flags assert 11 == sk.s_cb.cb_active assert sk.s_cb.cb_err is None nl_socket_free(sk) assert initial_fd_count == len(os.listdir('/proc/self/fd'))
def test_ctrl_cmd_getfamily_hex_dump(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 ./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; }; static void prefix_line(FILE *ofd, int prefix) { int i; for (i = 0; i < prefix; i++) fprintf(ofd, " "); } static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) { int i, a, c, limit; char ascii[21] = {0}; limit = 16 - (prefix * 2); prefix_line(ofd, prefix); fprintf(ofd, " "); for (i = 0, a = 0, c = 0; i < len; i++) { int v = *(uint8_t *) (start + i); fprintf(ofd, "%02x ", v); ascii[a++] = isprint(v) ? v : '.'; if (++c >= limit) { fprintf(ofd, "%s\n", ascii); if (i < (len - 1)) { prefix_line(ofd, prefix); fprintf(ofd, " "); } a = c = 0; memset(ascii, 0, sizeof(ascii)); } } if (c != 0) { for (i = 0; i < (limit - c); i++) fprintf(ofd, " "); fprintf(ofd, "%s\n", ascii); } } struct ucred { pid_t pid; uid_t uid; gid_t gid; }; struct nl_msg { int nm_protocol; int nm_flags; struct sockaddr_nl nm_src; struct sockaddr_nl nm_dst; struct ucred nm_creds; struct nlmsghdr *nm_nlh; size_t nm_size; int nm_refcnt; }; static int callback_send(struct nl_sock *sk, struct nl_msg *msg) { printf("%d == msg.nm_protocol\n", msg->nm_protocol); printf("%d == msg.nm_flags\n", msg->nm_flags); printf("%d == msg.nm_src.nl_family\n", msg->nm_src.nl_family); printf("%d == msg.nm_src.nl_pid\n", msg->nm_src.nl_pid); printf("%d == msg.nm_src.nl_groups\n", msg->nm_src.nl_groups); printf("%d == msg.nm_dst.nl_family\n", msg->nm_dst.nl_family); printf("%d == msg.nm_dst.nl_pid\n", msg->nm_dst.nl_pid); printf("%d == msg.nm_dst.nl_groups\n", msg->nm_dst.nl_groups); printf("%d == msg.nm_creds.pid\n", msg->nm_creds.pid); printf("%d == msg.nm_creds.uid\n", msg->nm_creds.uid); printf("%d == msg.nm_creds.gid\n", msg->nm_creds.gid); printf("%d == msg.nm_nlh.nlmsg_type\n", msg->nm_nlh->nlmsg_type); printf("%d == msg.nm_nlh.nlmsg_flags\n", msg->nm_nlh->nlmsg_flags); printf("%d == msg.nm_nlh.nlmsg_pid\n", msg->nm_nlh->nlmsg_pid); printf("%d == msg.nm_size\n", msg->nm_size); printf("%d == msg.nm_refcnt\n", msg->nm_refcnt); struct iovec iov = { .iov_base = (void *) nlmsg_hdr(msg), .iov_len = nlmsg_hdr(msg)->nlmsg_len, }; dump_hex(stdout, iov.iov_base, iov.iov_len, 0); return nl_send_iovec(sk, msg, &iov, 1); } static int callback_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) { int n = nl_recv(sk, nla, buf, creds); dump_hex(stdout, (void *) *buf, n, 0); return n; } static int callback_recv_msg(struct nl_msg *msg, void *arg) { printf("%d == msg.nm_protocol\n", msg->nm_protocol); printf("%d == msg.nm_flags\n", msg->nm_flags); printf("%d == msg.nm_src.nl_family\n", msg->nm_src.nl_family); printf("%d == msg.nm_src.nl_pid\n", msg->nm_src.nl_pid); printf("%d == msg.nm_src.nl_groups\n", msg->nm_src.nl_groups); printf("%d == msg.nm_dst.nl_family\n", msg->nm_dst.nl_family); printf("%d == msg.nm_dst.nl_pid\n", msg->nm_dst.nl_pid); printf("%d == msg.nm_dst.nl_groups\n", msg->nm_dst.nl_groups); printf("%d == msg.nm_creds.pid\n", msg->nm_creds.pid); printf("%d == msg.nm_creds.uid\n", msg->nm_creds.uid); printf("%d == msg.nm_creds.gid\n", msg->nm_creds.gid); printf("%d == msg.nm_nlh.nlmsg_type\n", msg->nm_nlh->nlmsg_type); printf("%d == msg.nm_nlh.nlmsg_flags\n", msg->nm_nlh->nlmsg_flags); printf("%d == msg.nm_nlh.nlmsg_pid\n", msg->nm_nlh->nlmsg_pid); printf("%d == msg.nm_size\n", msg->nm_size); printf("%d == msg.nm_refcnt\n", msg->nm_refcnt); dump_hex(stdout, (char *) msg->nm_nlh, nlmsg_datalen(msg->nm_nlh), 0); return NL_OK; } int main() { struct nl_sock *sk = nl_socket_alloc(); nl_cb_overwrite_send(sk->s_cb, callback_send); nl_cb_overwrite_recv(sk->s_cb, callback_recv); printf("%d == genl_connect(sk)\n", genl_connect(sk)); struct genl_family *ret = (struct genl_family *) genl_family_alloc(); genl_family_set_name(ret, "nl80211"); struct nl_msg *msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1); nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, "nl80211"); nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback_recv_msg, NULL); printf("%d == nl_send_auto(sk, msg)\n", nl_send_auto(sk, msg)); printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // 0 == genl_connect(sk) // nl_object_alloc: Allocated new object 0x2b50b8 // __nlmsg_alloc: msg 0x2b5110: Allocated new message, maxlen=4096 // nlmsg_put: msg 0x2b5110: Added netlink header type=16, flags=0, pid=0, seq=0 // nlmsg_reserve: msg 0x2b5110: Reserved 4 (4) bytes, pad=4, nlmsg_len=20 // genlmsg_put: msg 0x2b5110: Added generic netlink header cmd=3 version=1 // nla_reserve: msg 0x2b5110: attr <0x2b5164> 2: Reserved 12 (8) bytes at offset +4 nlmsg_len=32 // nla_put: msg 0x2b5110: attr <0x2b5164> 2: Wrote 8 bytes at offset +4 // 16 == msg.nm_protocol // 0 == msg.nm_flags // 0 == msg.nm_src.nl_family // 0 == msg.nm_src.nl_pid // 0 == msg.nm_src.nl_groups // 0 == msg.nm_dst.nl_family // 0 == msg.nm_dst.nl_pid // 0 == msg.nm_dst.nl_groups // 0 == msg.nm_creds.pid // 0 == msg.nm_creds.uid // 0 == msg.nm_creds.gid // 16 == msg.nm_nlh.nlmsg_type // 5 == msg.nm_nlh.nlmsg_flags // 14272 == msg.nm_nlh.nlmsg_pid // 4096 == msg.nm_size // 1 == msg.nm_refcnt // 20 00 00 00 10 00 05 00 af aa f6 54 c0 37 00 00 ..........T.7.. // 03 01 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211. // nl_sendmsg: sent 32 bytes // 32 == nl_send_auto(sk, msg) // recvmsgs: Attempting to read from 0x2b5080 // 2c 07 00 00 10 00 00 00 af aa f6 54 c0 37 00 00 ,..........T.7.. // 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211. // 06 00 01 00 16 00 00 00 08 00 03 00 01 00 00 00 ................ // 08 00 04 00 00 00 00 00 08 00 05 00 d5 00 00 00 ................ // 6c 06 06 00 14 00 01 00 08 00 01 00 01 00 00 00 l............... // 08 00 02 00 0e 00 00 00 14 00 02 00 08 00 01 00 ................ // <trimmed> // 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config.......... // 04 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan.... // 1c 00 03 00 08 00 02 00 05 00 00 00 0f 00 01 00 ................ // 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory...... // 08 00 02 00 06 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme // 00 00 00 00 18 00 05 00 08 00 02 00 07 00 00 00 ................ // 0b 00 01 00 76 65 6e 64 6f 72 00 00 ....vendor.. // recvmsgs: recvmsgs(0x2b5080): Read 1836 bytes // recvmsgs: recvmsgs(0x2b5080): Processing valid message... // __nlmsg_alloc: msg 0x2ba160: Allocated new message, maxlen=1836 // 16 == msg.nm_protocol // 0 == msg.nm_flags // 16 == msg.nm_src.nl_family // 0 == msg.nm_src.nl_pid // 0 == msg.nm_src.nl_groups // 0 == msg.nm_dst.nl_family // 0 == msg.nm_dst.nl_pid // 0 == msg.nm_dst.nl_groups // 0 == msg.nm_creds.pid // 0 == msg.nm_creds.uid // 0 == msg.nm_creds.gid // 16 == msg.nm_nlh.nlmsg_type // 0 == msg.nm_nlh.nlmsg_flags // 14272 == msg.nm_nlh.nlmsg_pid // 1836 == msg.nm_size // 1 == msg.nm_refcnt // 2c 07 00 00 10 00 00 00 af aa f6 54 c0 37 00 00 ,..........T.7.. // 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211. // 06 00 01 00 16 00 00 00 08 00 03 00 01 00 00 00 ................ // 08 00 04 00 00 00 00 00 08 00 05 00 d5 00 00 00 ................ // 6c 06 06 00 14 00 01 00 08 00 01 00 01 00 00 00 l............... // 08 00 02 00 0e 00 00 00 14 00 02 00 08 00 01 00 ................ // <trimmed> // 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config.......... // 04 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan.... // 1c 00 03 00 08 00 02 00 05 00 00 00 0f 00 01 00 ................ // 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory...... // 08 00 02 00 06 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme // 00 00 00 00 18 00 05 00 08 00 02 00 ............ // nlmsg_free: Returned message reference 0x2ba160, 0 remaining // nlmsg_free: msg 0x2ba160: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ def callback_send(sk, msg): assert 16 == msg.nm_protocol assert 0 == msg.nm_flags assert 0 == msg.nm_src.nl_family assert 0 == msg.nm_src.nl_pid assert 0 == msg.nm_src.nl_groups assert 0 == msg.nm_dst.nl_family assert 0 == msg.nm_dst.nl_pid assert 0 == msg.nm_dst.nl_groups assert msg.nm_creds is None assert 16 == msg.nm_nlh.nlmsg_type assert 5 == msg.nm_nlh.nlmsg_flags assert 100 < msg.nm_nlh.nlmsg_pid assert 1 == msg.nm_refcnt hdr = nlmsg_hdr(msg) iov = hdr.bytearray[:hdr.nlmsg_len] dump_hex(logging.getLogger().debug, iov, len(iov), 0) return nl_send_iovec(sk, msg, iov, 1) def callback_recv(sk, nla, buf, creds): n = nl_recv(sk, nla, buf, creds) dump_hex(logging.getLogger().debug, buf, len(buf), 0) return n def callback_recv_msg(msg, _): assert 16 == msg.nm_protocol assert 0 == msg.nm_flags assert 16 == msg.nm_src.nl_family assert 0 == msg.nm_src.nl_pid assert 0 == msg.nm_src.nl_groups assert 0 == msg.nm_dst.nl_family assert 0 == msg.nm_dst.nl_pid assert 0 == msg.nm_dst.nl_groups assert msg.nm_creds is None assert 16 == msg.nm_nlh.nlmsg_type assert 0 == msg.nm_nlh.nlmsg_flags assert 100 < msg.nm_nlh.nlmsg_pid assert 1000 < msg.nm_size assert 1 == msg.nm_refcnt dump_hex(logging.getLogger().debug, msg.nm_nlh.bytearray, nlmsg_datalen(msg.nm_nlh), 0) return NL_OK del log[:] sk_main = nl_socket_alloc() nl_cb_overwrite_send(sk_main.s_cb, callback_send) nl_cb_overwrite_recv(sk_main.s_cb, callback_recv) genl_connect(sk_main) ret = genl_family_alloc() genl_family_set_name(ret, b'nl80211') msg_main = nlmsg_alloc() genlmsg_put(msg_main, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1) nla_put_string(msg_main, CTRL_ATTR_FAMILY_NAME, b'nl80211') nl_socket_modify_cb(sk_main, NL_CB_VALID, NL_CB_CUSTOM, callback_recv_msg, None) assert 32 == nl_send_auto(sk_main, msg_main) assert 0 == nl_recvmsgs_default(sk_main) nl_socket_free(sk_main) assert match('nl_object_alloc: Allocated new object 0x[a-f0-9]+', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match( 'nlmsg_put: msg 0x[a-f0-9]+: Added netlink header type=16, flags=0, pid=0, seq=0', log, True) assert match( 'nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(4\) bytes, pad=4, nlmsg_len=20', log, True) assert match( 'genlmsg_put: msg 0x[a-f0-9]+: Added generic netlink header cmd=3 version=1', log, True) assert match( 'nla_reserve: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Reserved 12 \(8\) bytes at offset \+4 nlmsg_len=32', log, True) assert match( 'nla_put: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Wrote 8 bytes at offset \+4', log, True) assert match( 'dump_hex: 20 00 00 00 10 00 05 00 .. .. .. .. .. .. 00 00 ...............', log, True) assert match( 'dump_hex: 03 01 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211.', log) assert match('nl_sendmsg: sent 32 bytes', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match( 'dump_hex: .. .. 00 00 10 00 00 00 .. .. .. .. .. .. 00 00 ................', log, True) assert match( 'dump_hex: 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211.', log) assert match( 'dump_hex: 06 00 01 00 .. 00 00 00 08 00 03 00 01 00 00 00 ................', log, True) assert match( 'dump_hex: 08 00 04 00 00 00 00 00 08 00 05 00 .. 00 00 00 ................', log, True) for i in range(len(log)): if re.match(r'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log[i]): log = log[i:] break assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{3,} bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match( 'dump_hex: .. .. 00 00 10 00 00 00 .. .. .. .. .. .. 00 00 ................', log, True) assert match( 'dump_hex: 01 02 00 00 0c 00 02 00 6e 6c 38 30 32 31 31 00 ........nl80211.', log) assert match( 'dump_hex: 06 00 01 00 .. 00 00 00 08 00 03 00 01 00 00 00 ................', log, True) assert match( 'dump_hex: 08 00 04 00 00 00 00 00 08 00 05 00 .. 00 00 00 ................', log, True) while log and log[0].startswith('dump_hex:'): log.pop(0) assert not log
def test_multipart(log, ifaces): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=debug ./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. printf("Begin main()\n"); struct nl_sock *sk = nl_socket_alloc(); printf("Allocated socket.\n"); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", nl_connect(sk, NETLINK_ROUTE)); struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; int ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); printf("Bytes Sent: %d\n", ret); // Retrieve kernel's response. printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0x1bbe0b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x1bbe0b8: Allocated new simple message // nlmsg_reserve: msg 0x1bbe0b8: Reserved 4 (1) bytes, pad=4, nlmsg_len=20 // nlmsg_append: msg 0x1bbe0b8: Appended 1 bytes with padding 4 // -- Debug: Sent Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 20 // .type = 18 <0x12> // .flags = 773 <REQUEST,ACK,ROOT,MATCH> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 4 octets // 11 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // nl_sendmsg: sent 20 bytes // nlmsg_free: Returned message reference 0x1bbe0b8, 0 remaining // nlmsg_free: msg 0x1bbe0b8: Freed // Bytes Sent: 20 // recvmsgs: Attempting to read from 0x1bbe080 // recvmsgs: recvmsgs(0x1bbe080): Read 3364 bytes // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=1116 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1116 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 1100 octets // 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I....... // <trimmed> // 00 00 00 00 00 00 00 00 00 00 00 00 ............ // --------------------------- END NETLINK MESSAGE --------------------------- // -- Debug: Unhandled Valid message: type=0x10 length=1116 flags=<MULTI> sequence-nr=1424053819 pid=18409 // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=1124 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1124 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 1108 octets // 00 00 01 00 02 00 00 00 43 10 01 00 00 00 00 00 ........C....... // <trimmed> // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // -- Debug: Unhandled Valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424053819 pid=18409 // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=1124 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1124 // .type = 16 <0x10> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 1108 octets // 00 00 01 00 03 00 00 00 03 10 00 00 00 00 00 00 ................ // <trimmed> // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // -- Debug: Unhandled Valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424053819 pid=18409 // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // recvmsgs: Attempting to read from 0x1bbe080 // recvmsgs: recvmsgs(0x1bbe080): Read 20 bytes // recvmsgs: recvmsgs(0x1bbe080): Processing valid message... // __nlmsg_alloc: msg 0x1bc20c0: Allocated new message, maxlen=20 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 20 // .type = 3 <DONE> // .flags = 2 <MULTI> // .seq = 1424053819 // .port = 18409 // [PAYLOAD] 4 octets // 00 00 00 00 .... // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x1bbe080): Increased expected sequence number to 1424053820 // -- Debug: End of multipart message block: type=DONE length=20 flags=<MULTI> sequence-nr=1424053819 pid=18409 // nlmsg_free: Returned message reference 0x1bc20c0, 0 remaining // nlmsg_free: msg 0x1bc20c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match( 'nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match( 'nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(1\) bytes, pad=4, nlmsg_len=20', log, True) assert match( 'nlmsg_append: msg 0x[a-f0-9]+: Appended 1 bytes with padding 4', log, True) assert match('nl_msg_out_handler_debug: -- Debug: Sent Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 20', log) assert match('print_hdr: .type = 18 <0x12>', log) assert match('print_hdr: .flags = 773 <REQUEST,ACK,ROOT,MATCH>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: [PAYLOAD] 4 octets', log) assert match( 'dump_hex: 11 00 00 00 ....', log) assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_sendmsg: sent 20 bytes', log) assert not log assert 0 == nl_recvmsgs_default(sk) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = \d{3,}', log, True) assert match('print_hdr: .type = 16 <0x10>', log) assert match('print_hdr: .flags = 2 <MULTI>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: \[PAYLOAD\] \d{3,} octets', log, True) assert match( 'dump_hex: 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I.......', log) assert match( 'dump_hex: 07 00 03 00 6c 6f 00 00 08 00 0d 00 00 00 00 00 ....lo..........', log) assert match( 'dump_hex: 05 00 10 00 00 00 00 00 05 00 11 00 00 00 00 00 ................', log) for _ in ifaces: # Done testing this payload. Differs too much between Travis and Raspbian, and probably others. rem = log.index( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------' ) assert 20 < rem # At least check that there were a lot of log statements skipped. log = log[rem:] assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match( 'nl_valid_handler_debug: -- Debug: Unhandled Valid message: type=0x10 length=\d{3,} flags=<MULTI> ' 'sequence-nr=\d{10,} pid=\d{3,}', log, True) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 20 bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=20', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 20', log) assert match('print_hdr: .type = 3 <DONE>', log) assert match('print_hdr: .flags = 2 <MULTI>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_msg: [PAYLOAD] 4 octets', log) assert match( 'dump_hex: 00 00 00 00 ....', log) assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) assert match( 'nl_finish_handler_debug: -- Debug: End of multipart message block: type=DONE length=20 flags=<MULTI> ' 'sequence-nr=\d{10,} pid=\d{3,}', log, True) nl_socket_free(sk) assert not log
def test_error(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=debug ./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. printf("Begin main()\n"); struct nl_sock *sk = nl_socket_alloc(); printf("Allocated socket.\n"); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", 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. printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0x3df0b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0x3df0b8: Allocated new simple message // -- Debug: Sent Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 16 // .type = 0 <0x0> // .flags = 5 <REQUEST,ACK> // .seq = 1423967746 // .port = 29930 // --------------------------- END NETLINK MESSAGE --------------------------- // nl_sendmsg: sent 16 bytes // nlmsg_free: Returned message reference 0x3df0b8, 0 remaining // nlmsg_free: msg 0x3df0b8: Freed // Bytes Sent: 16 // recvmsgs: Attempting to read from 0x3df080 // recvmsgs: recvmsgs(0x3df080): Read 36 bytes // recvmsgs: recvmsgs(0x3df080): Processing valid message... // __nlmsg_alloc: msg 0x3e30c0: Allocated new message, maxlen=36 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 36 // .type = 2 <ERROR> // .flags = 0 <> // .seq = 1423967746 // .port = 29930 // [ERRORMSG] 20 octets // .error = 0 "Success" // [ORIGINAL MESSAGE] 16 octets // __nlmsg_alloc: msg 0x3e3128: Allocated new message, maxlen=4096 // .nlmsg_len = 16 // .type = 0 <0x0> // .flags = 5 <REQUEST,ACK> // .seq = 1423967746 // .port = 29930 // nlmsg_free: Returned message reference 0x3e3128, 0 remaining // nlmsg_free: msg 0x3e3128: Freed // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x3df080): Increased expected sequence number to 1423967746 // -- Debug: ACK: type=ERROR length=36 flags=<> sequence-nr=1423967746 pid=29930 // nlmsg_free: Returned message reference 0x3e30c0, 0 remaining // nlmsg_free: msg 0x3e30c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) assert 16 == nl_send_simple(sk, 0, NLM_F_REQUEST, None) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match( 'nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match('nl_msg_out_handler_debug: -- Debug: Sent Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 16', log) assert match('print_hdr: .type = 0 <0x0>', log) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_sendmsg: sent 16 bytes', log) assert not log assert 0 == nl_recvmsgs_default(sk) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 36 bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=36', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match( 'nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 36', log) assert match('print_hdr: .type = 2 <ERROR>', log) assert match('print_hdr: .flags = 0 <>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('dump_error_msg: [ERRORMSG] 20 octets', log) assert match('dump_error_msg: .error = 0 "Success"', log) assert match('dump_error_msg: [ORIGINAL MESSAGE] 16 octets', log) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('print_hdr: .nlmsg_len = 16', log) assert match('print_hdr: .type = 0 <0x0>', log) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match( 'nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) assert match( 'nl_ack_handler_debug: -- Debug: ACK: type=ERROR length=36 flags=<> sequence-nr=\d{10,} pid=\d{3,}', log, True) nl_socket_free(sk) assert not log
def test_genl_ctrl_resolve(log): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && NLDBG=4 NLCB=debug ./a.out #include <netlink/msg.h> int main() { struct nl_sock *sk = nl_socket_alloc(); printf("%d == genl_connect(sk)\n", genl_connect(sk)); int driver_id = genl_ctrl_resolve(sk, "nl80211"); printf("%d == driver_id\n", driver_id); nl_socket_free(sk); return 0; } // Expected output (trimmed): // nl_cache_mngt_register: Registered cache operations genl/family // 0 == genl_connect(sk) // nl_object_alloc: Allocated new object 0x6f90b8 // __nlmsg_alloc: msg 0x6f9110: Allocated new message, maxlen=4096 // nlmsg_put: msg 0x6f9110: Added netlink header type=16, flags=0, pid=0, seq=0 // nlmsg_reserve: msg 0x6f9110: Reserved 4 (4) bytes, pad=4, nlmsg_len=20 // genlmsg_put: msg 0x6f9110: Added generic netlink header cmd=3 version=1 // nla_reserve: msg 0x6f9110: attr <0x6f9164> 2: Reserved 12 (8) bytes at offset +4 nlmsg_len=32 // nla_put: msg 0x6f9110: attr <0x6f9164> 2: Wrote 8 bytes at offset +4 // -- Debug: Sent Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 32 // .type = 16 <genl/family::nlctrl> // .flags = 5 <REQUEST,ACK> // .seq = 1425769691 // .port = 2568 // [GENERIC NETLINK HEADER] 4 octets // .cmd = 3 // .version = 1 // .unused = 0 // [ATTR 02] 8 octets // 6e 6c 38 30 32 31 31 00 nl80211. // --------------------------- END NETLINK MESSAGE --------------------------- // nl_sendmsg: sent 32 bytes // recvmsgs: Attempting to read from 0x6f9080 // recvmsgs: recvmsgs(0x6f9080): Read 1836 bytes // recvmsgs: recvmsgs(0x6f9080): Processing valid message... // __nlmsg_alloc: msg 0x6fe1d8: Allocated new message, maxlen=1836 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 1836 // .type = 16 <genl/family::nlctrl> // .flags = 0 <> // .seq = 1425769691 // .port = 2568 // [GENERIC NETLINK HEADER] 4 octets // .cmd = 1 // .version = 2 // .unused = 0 // [ATTR 02] 8 octets // 6e 6c 38 30 32 31 31 00 nl80211. // [ATTR 01] 2 octets // 16 00 .. // [PADDING] 2 octets // 00 00 .. // [ATTR 03] 4 octets // 01 00 00 00 .... // [ATTR 04] 4 octets // 00 00 00 00 .... // [ATTR 05] 4 octets // d5 00 00 00 .... // [ATTR 06] 1640 octets // 14 00 01 00 08 00 01 00 01 00 00 00 08 00 02 00 ................ // <trimmed> // 08 00 02 00 0b 00 00 00 ........ // [ATTR 07] 124 octets // 18 00 01 00 08 00 02 00 03 00 00 00 0b 00 01 00 ................ // 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config.......... // 04 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan.... // 1c 00 03 00 08 00 02 00 05 00 00 00 0f 00 01 00 ................ // 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory...... // 08 00 02 00 06 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme // 00 00 00 00 18 00 05 00 08 00 02 00 07 00 00 00 ................ // 0b 00 01 00 76 65 6e 64 6f 72 00 00 ....vendor.. // --------------------------- END NETLINK MESSAGE --------------------------- // nlmsg_free: Returned message reference 0x6fe1d8, 0 remaining // nlmsg_free: msg 0x6fe1d8: Freed // recvmsgs: Attempting to read from 0x6f9080 // recvmsgs: recvmsgs(0x6f9080): Read 36 bytes // recvmsgs: recvmsgs(0x6f9080): Processing valid message... // __nlmsg_alloc: msg 0x6fe1d8: Allocated new message, maxlen=36 // -- Debug: Received Message: // -------------------------- BEGIN NETLINK MESSAGE --------------------------- // [NETLINK HEADER] 16 octets // .nlmsg_len = 36 // .type = 2 <ERROR> // .flags = 0 <> // .seq = 1425769691 // .port = 2568 // [ERRORMSG] 20 octets // .error = 0 "Success" // [ORIGINAL MESSAGE] 16 octets // __nlmsg_alloc: msg 0x6fe2b8: Allocated new message, maxlen=4096 // .nlmsg_len = 16 // .type = 16 <0x10> // .flags = 5 <REQUEST,ACK> // .seq = 1425769691 // .port = 2568 // nlmsg_free: Returned message reference 0x6fe2b8, 0 remaining // nlmsg_free: msg 0x6fe2b8: Freed // --------------------------- END NETLINK MESSAGE --------------------------- // recvmsgs: recvmsgs(0x6f9080): Increased expected sequence number to 1425769692 // nlmsg_free: Returned message reference 0x6fe1d8, 0 remaining // nlmsg_free: msg 0x6fe1d8: Freed // nlmsg_free: Returned message reference 0x6f9110, 0 remaining // nlmsg_free: msg 0x6f9110: Freed // nl_object_put: Returned object reference 0x6f90b8, 0 remaining // nl_object_free: Freed object 0x6f90b8 // 22 == driver_id // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() assert 0 == genl_connect(sk) assert not log assert 20 <= genl_ctrl_resolve(sk, b'nl80211') nl_socket_free(sk) assert match('nl_object_alloc: Allocated new object 0x[a-f0-9]+', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('nlmsg_put: msg 0x[a-f0-9]+: Added netlink header type=16, flags=0, pid=0, seq=0', log, True) assert match('nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(4\) bytes, pad=4, nlmsg_len=20', log, True) assert match('genlmsg_put: msg 0x[a-f0-9]+: Added generic netlink header cmd=3 version=1', log, True) assert match( 'nla_reserve: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Reserved 12 \(8\) bytes at offset \+4 nlmsg_len=32', log, True) assert match('nla_put: msg 0x[a-f0-9]+: attr <0x[a-f0-9]+> 2: Wrote 8 bytes at offset \+4', log, True) assert match('nl_msg_out_handler_debug: -- Debug: Sent Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 32', log) assert match('print_hdr: .type = 16 <genl/family::nlctrl>', log) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_genl_hdr: [GENERIC NETLINK HEADER] 4 octets', log) assert match('print_genl_hdr: .cmd = 3', log) assert match('print_genl_hdr: .version = 1', log) assert match('print_genl_hdr: .unused = 0', log) assert match('dump_attrs: [ATTR 02] 8 octets', log) assert match('dump_hex: 6e 6c 38 30 32 31 31 00 nl80211.', log) assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('nl_sendmsg: sent 32 bytes', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{3,} bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = \d{3,}', log, True) assert match('print_hdr: .type = 16 <genl/family::nlctrl>', log) assert match('print_hdr: .flags = 0 <>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('print_genl_hdr: [GENERIC NETLINK HEADER] 4 octets', log) assert match('print_genl_hdr: .cmd = 1', log) assert match('print_genl_hdr: .version = 2', log) assert match('print_genl_hdr: .unused = 0', log) assert match('dump_attrs: [ATTR 02] 8 octets', log) assert match('dump_hex: 6e 6c 38 30 32 31 31 00 nl80211.', log) assert match('dump_attrs: [ATTR 01] 2 octets', log) assert match('dump_hex: .. 00 ..', log, True) assert match('dump_attrs: [PADDING] 2 octets', log) assert match('dump_hex: 00 00 ..', log) assert match('dump_attrs: [ATTR 03] 4 octets', log) assert match('dump_hex: 01 00 00 00 ....', log) assert match('dump_attrs: [ATTR 04] 4 octets', log) assert match('dump_hex: 00 00 00 00 ....', log) assert match('dump_attrs: [ATTR 05] 4 octets', log) assert match('dump_hex: .. 00 00 00 ....', log, True) assert match('dump_attrs: \[ATTR 06\] \d{4,} octets', log, True) assert match('dump_hex: 14 00 01 00 08 00 01 00 01 00 00 00 08 00 02 00 ................', log) # Done testing this payload. Too big. for line in log: if line.startswith('dump_hex'): continue rem = log.index(line) assert 20 < rem # At least check that there were a lot of log statements skipped. log = log[rem:] break assert match('dump_attrs: \[ATTR 07\] \d{3,} octets', log, True) assert match('dump_hex: 18 00 01 00 08 00 02 00 .. 00 00 00 0b 00 01 00 ................', log, True) assert match('dump_hex: 63 6f 6e 66 69 67 00 00 18 00 02 00 08 00 02 00 config..........', log) assert match('dump_hex: .. 00 00 00 09 00 01 00 73 63 61 6e 00 00 00 00 ........scan....', log, True) assert match('dump_hex: 1c 00 03 00 08 00 02 00 .. 00 00 00 0f 00 01 00 ................', log, True) assert match('dump_hex: 72 65 67 75 6c 61 74 6f 72 79 00 00 18 00 04 00 regulatory......', log) assert match('dump_hex: 08 00 02 00 .. 00 00 00 09 00 01 00 6d 6c 6d 65 ............mlme', log, True) rem = log.index('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------') log = log[rem:] assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 36 bytes', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=36', log, True) assert match('nl_msg_in_handler_debug: -- Debug: Received Message:', log) assert match('nl_msg_dump: -------------------------- BEGIN NETLINK MESSAGE ---------------------------', log) assert match('nl_msg_dump: [NETLINK HEADER] 16 octets', log) assert match('print_hdr: .nlmsg_len = 36', log) assert match('print_hdr: .type = 2 <ERROR>', log) assert match('print_hdr: .flags = 0 <>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('dump_error_msg: [ERRORMSG] 20 octets', log) assert match('dump_error_msg: .error = 0 "Success"', log) assert match('dump_error_msg: [ORIGINAL MESSAGE] 16 octets', log) assert match('nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match('print_hdr: .nlmsg_len = 16', log) assert match('print_hdr: .type = 16 <0x[a-f0-9]+>', log, True) assert match('print_hdr: .flags = 5 <REQUEST,ACK>', log) assert match('print_hdr: .seq = \d{10}', log, True) assert match('print_hdr: .port = \d{3,}', log, True) assert match('nl_msg_dump: --------------------------- END NETLINK MESSAGE ---------------------------', log) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{10}', log, True) assert not log
def test_multipart_verbose(log, ifaces): """Expected output (trimmed). // nl_cache_mngt_register: Registered cache operations genl/family // Begin main() // Allocated socket. // 0 == nl_connect(sk, NETLINK_ROUTE) // __nlmsg_alloc: msg 0xa180b8: Allocated new message, maxlen=4096 // nlmsg_alloc_simple: msg 0xa180b8: Allocated new simple message // nlmsg_reserve: msg 0xa180b8: Reserved 4 (1) bytes, pad=4, nlmsg_len=20 // nlmsg_append: msg 0xa180b8: Appended 1 bytes with padding 4 // nl_sendmsg: sent 20 bytes // nlmsg_free: Returned message reference 0xa180b8, 0 remaining // nlmsg_free: msg 0xa180b8: Freed // Bytes Sent: 20 // recvmsgs: Attempting to read from 0xa18080 // recvmsgs: recvmsgs(0xa18080): Read 3364 bytes // recvmsgs: recvmsgs(0xa18080): Processing valid message... // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=1116 // -- Warning: unhandled valid message: type=0x10 length=1116 flags=<MULTI> sequence-nr=1424132449 pid=5810 // recvmsgs: recvmsgs(0xa18080): Processing valid message... // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=1124 // -- Warning: unhandled valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424132449 pid=5810 // recvmsgs: recvmsgs(0xa18080): Processing valid message... // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=1124 // -- Warning: unhandled valid message: type=0x10 length=1124 flags=<MULTI> sequence-nr=1424132449 pid=5810 // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // recvmsgs: Attempting to read from 0xa18080 // recvmsgs: recvmsgs(0xa18080): Read 20 bytes // recvmsgs: recvmsgs(0xa18080): Processing valid message... // __nlmsg_alloc: msg 0xa1c0c0: Allocated new message, maxlen=20 // recvmsgs: recvmsgs(0xa18080): Increased expected sequence number to 1424132450 // nlmsg_free: Returned message reference 0xa1c0c0, 0 remaining // nlmsg_free: msg 0xa1c0c0: Freed // 0 == nl_recvmsgs_default(sk) // nl_cache_mngt_unregister: Unregistered cache operations genl/family """ del log[:] sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=4096', log, True) assert match( 'nlmsg_alloc_simple: msg 0x[a-f0-9]+: Allocated new simple message', log, True) assert match( 'nlmsg_reserve: msg 0x[a-f0-9]+: Reserved 4 \(1\) bytes, pad=4, nlmsg_len=20', log, True) assert match( 'nlmsg_append: msg 0x[a-f0-9]+: Appended 1 bytes with padding 4', log, True) assert match('nl_sendmsg: sent 20 bytes', log) assert not log assert 0 == nl_recvmsgs_default(sk) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) for _ in ifaces: if 'Attempting to read' in log[0]: # Lots of network interfaces on this host. assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read \d{4,} bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=\d{3,}', log, True) assert match( 'nl_valid_handler_verbose: -- Warning: unhandled valid message: type=0x10 length=\d{3,} ' 'flags=<MULTI> sequence-nr=\d{10,} pid=\d{3,}', log, True) assert match('recvmsgs: Attempting to read from 0x[a-f0-9]+', log, True) assert match('recvmsgs: recvmsgs\(0x[a-f0-9]+\): Read 20 bytes', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Processing valid message...', log, True) assert match( 'nlmsg_alloc: msg 0x[a-f0-9]+: Allocated new message, maxlen=20', log, True) assert match( 'recvmsgs: recvmsgs\(0x[a-f0-9]+\): Increased expected sequence number to \d{4,}', log, True) nl_socket_free(sk) assert not log
def test_genl_ctrl_probe_by_name(): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && ./a.out #include <netlink/msg.h> #define NL_NO_AUTO_ACK (1<<4) 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; }; struct genl_family { int ce_refcnt; struct nl_object_ops *ce_ops; struct nl_cache *ce_cache; struct nl_list_head ce_list; int ce_msgtype; int ce_flags; uint32_t ce_mask; uint16_t gf_id; char gf_name[GENL_NAMSIZ]; uint32_t gf_version; uint32_t gf_hdrsize; uint32_t gf_maxattr; struct nl_list_head gf_ops; struct nl_list_head gf_mc_grps; }; static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, .maxlen = GENL_NAMSIZ }, [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, }; static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = { [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING }, [CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 }, }; static inline int wait_for_ack(struct nl_sock *sk) { if (sk->s_flags & NL_NO_AUTO_ACK) return 0; else return nl_wait_for_ack(sk); } static int parse_mcast_grps(struct genl_family *family, struct nlattr *grp_attr) { struct nlattr *nla; int remaining, err; nla_for_each_nested(nla, grp_attr, remaining) { struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1]; int id; const char *name; err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla, family_grp_policy); if (err < 0) goto errout; if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) { err = -NLE_MISSING_ATTR; goto errout; } id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) { err = -NLE_MISSING_ATTR; goto errout; } name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]); err = genl_family_add_grp(family, id, name); if (err < 0) goto errout; } err = 0; errout: return err; } static int probe_response(struct nl_msg *msg, void *arg) { struct nlattr *tb[CTRL_ATTR_MAX+1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct genl_family *ret = (struct genl_family *)arg; if (genlmsg_parse(nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) return NL_SKIP; if (tb[CTRL_ATTR_FAMILY_ID]) genl_family_set_id(ret, nla_get_u16(tb[CTRL_ATTR_FAMILY_ID])); if (tb[CTRL_ATTR_MCAST_GROUPS]) if (parse_mcast_grps(ret, tb[CTRL_ATTR_MCAST_GROUPS]) < 0) return NL_SKIP; return NL_STOP; } static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk, const char *name) { struct nl_msg *msg; struct genl_family *ret; struct nl_cb *cb, *orig; int rc; ret = (struct genl_family *) genl_family_alloc(); if (!ret) goto out; genl_family_set_name(ret, name); msg = nlmsg_alloc(); if (!msg) goto out_fam_free; if (!(orig = nl_socket_get_cb(sk))) goto out_msg_free; cb = nl_cb_clone(orig); nl_cb_put(orig); if (!cb) goto out_msg_free; if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) goto out_cb_free; if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name) < 0) goto out_cb_free; rc = nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, (void *) ret); if (rc < 0) goto out_cb_free; rc = nl_send_auto_complete(sk, msg); if (rc < 0) goto out_cb_free; rc = nl_recvmsgs(sk, cb); if (rc < 0) goto out_cb_free; rc = wait_for_ack(sk); if (rc < 0) goto out_cb_free; if (genl_family_get_id(ret) != 0) { nlmsg_free(msg); nl_cb_put(cb); return ret; } out_cb_free: nl_cb_put(cb); out_msg_free: nlmsg_free(msg); out_fam_free: genl_family_put(ret); ret = NULL; out: return ret; } int main() { struct nl_sock *sk = nl_socket_alloc(); printf("%d == genl_connect(sk)\n", genl_connect(sk)); struct genl_family *ret = genl_ctrl_probe_by_name(sk, "nl80211"); printf("%d == ret.ce_msgtype\n", ret->ce_msgtype); printf("%d == ret.ce_flags\n", ret->ce_flags); printf("%d == ret.ce_mask\n", ret->ce_mask); printf("%d == ret.gf_id\n", ret->gf_id); printf("%s == ret.gf_name\n", ret->gf_name); printf("%d == ret.gf_version\n", ret->gf_version); printf("%d == ret.gf_hdrsize\n", ret->gf_hdrsize); printf("%d == ret.gf_maxattr\n", ret->gf_maxattr); nl_socket_free(sk); return 0; } // Expected output: // 0 == genl_connect(sk) // 0 == ret.ce_msgtype // 0 == ret.ce_flags // 3 == ret.ce_mask // 22 == ret.gf_id // nl80211 == ret.gf_name // 0 == ret.gf_version // 0 == ret.gf_hdrsize // 0 == ret.gf_maxattr """ sk = nl_socket_alloc() assert 0 == genl_connect(sk) ret = genl_ctrl_probe_by_name(sk, b'nl80211') assert 0 == ret.ce_msgtype assert 0 == ret.ce_flags assert 3 == ret.ce_mask assert 20 <= ret.gf_id assert b'nl80211' == ret.gf_name assert 0 == ret.gf_version assert 0 == ret.gf_hdrsize assert 0 == ret.gf_maxattr nl_socket_free(sk)
def test_list_interfaces(ifacesi): r"""C code to test against. // gcc a.c $(pkg-config --cflags --libs libnl-genl-3.0) && ./a.out #include <netlink/msg.h> static int callback(struct nl_msg *msg, void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct ifinfomsg *iface = NLMSG_DATA(nlh); struct rtattr *hdr = IFLA_RTA(iface); int remaining = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface)); while (RTA_OK(hdr, remaining)) { if (hdr->rta_type == IFLA_IFNAME) { printf("Found network interface %d: %s\n", iface->ifi_index, (char *) RTA_DATA(hdr)); } hdr = RTA_NEXT(hdr, remaining); } return NL_OK; } int main() { // Send data to the kernel. struct nl_sock *sk = nl_socket_alloc(); printf("%d == nl_connect(sk, NETLINK_ROUTE)\n", nl_connect(sk, NETLINK_ROUTE)); struct rtgenmsg rt_hdr = { .rtgen_family = AF_PACKET, }; int ret = nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr)); printf("Bytes Sent: %d\n", ret); // Retrieve kernel's response. nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, NULL); printf("%d == nl_recvmsgs_default(sk)\n", nl_recvmsgs_default(sk)); nl_socket_free(sk); return 0; } // Expected output: // 0 == nl_connect(sk, NETLINK_ROUTE) // Bytes Sent: 20 // Found network interface 1: lo // Found network interface 2: eth0 // Found network interface 4: wlan0 // 0 == nl_recvmsgs_default(sk) """ got_something = dict() 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 sk = nl_socket_alloc() nl_connect(sk, NETLINK_ROUTE) rt_hdr = rtgenmsg(rtgen_family=socket.AF_PACKET) assert 20 == nl_send_simple(sk, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, rt_hdr, rt_hdr.SIZEOF) assert 0 == nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, callback, got_something) assert 0 == nl_recvmsgs_default(sk) assert dict(ifacesi) == got_something nl_socket_free(sk)