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 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 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 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 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_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)
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_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_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_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_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