WARNING: The best way to learn nse-pcap is to read my scripts promiscuous.nse and p0f.nse New features in NSE: I extended host structure: host.directly_connected (boolean) - whether the host is directly connected host.mac_addr (6 byte binary string) - 48bit ethernet address of destination or nil if host is not directly connected host.mac_addr_src (6 byte binary string) - 48bit ethernet address of our network card (or spoffed) we are going to send packets from this MAC address host.interface (string) - dnet-style interface name through which we are connecting to the host host.bin_ip (4 byte binary string) - binary ip address of target host.bin_ip_src (4 byte binary string) - binary ip address of our nic (or spoofed address) New dnet structure: dnet:open_ethernet(interface_name) interface_name - dnet style interface name - openes ethernet device to send packets from it dnet:send_ethernet(packet) packet - binary string with layer2 headers +upper layers - sends ethernet packet using current dnet device dnet:close_ethernet() - closes ethernet device Dnet devices are cached. So if you'll open some interface in more than one lua thread, they use one phisical descriptor. Descriptor is closed only when no process is using it. Extended nmap structure: nmap.get_interface_link(interface_name) (string) interface_name - dnet style interface name - it returns link layer2 name. Currently result can be one of this: 'ethernet' 'loopback' 'p2p' nil Extended nsock structure: nsock.clock_ms() (number) returns milliseconds since epoch. nsock:pcap_open(device, snaplen, promisc, test_function, bpf) device - dnet-style interface name snaplen - max length of packet to be captured (like '-s' in tcpdump) promisc - 1 if device should be opened in promiscuous mode, 0 otherwise test_function - callback function, that will return hash from packet bpf - Berkeley packet filter expression (like in tcpdump) - openes pcap device nsock:pcap_close() - closes pcap device nsock:pcap_register(packet_hash) packet_hash - binary string that would be compared with received packet if the test will succeed than we'll receive packet if you want to receive all packets just pass empty string - no result value - this function tells pcap to start listening to packets. It's non-blocking function. It must be executed before nsock:pcap_receive. nsock:pcap_receive() - result is tuple that contains if the packet is received true, packet_len, l2_data, l3_data - packet_len is length of original packet (but you can receive less data depending on snaplen) - l2_data is data from second OSI layer, like ethernet headers - l3_data is data from third OSI layer, like ipv4 headers (remember that length(l2_data) + length(l3_data) == MIN(packet_len, snaplen),) if error occured nil, error_description, nil, nil - this function blocks until packet is received. it must be executed after registering to pcap, using nsock:pcap_register Pcap devices are also cached. It would be performance nightmare if we'll open single pcapdescriptor for every lua thread. So please don't use host specific pcap filters. To distribute packets to specific lua threads we created the idea od 'packet_hash'. After pcap layer receives packet, your callback function (test_function) will be executed with current packet as parameter. Your callback will return some kind of packet_hash. After this, every lua thread that registered to listen with packet_hash that matches current will be restored. One physical packet can be passed to many lua threads. Remember, packet_hash registered by the nsock:pcap_register must be binary identical to packet_hash returned by your callback function. For example let's set match packets for source/dest mac (it's pseudo-code, from promiscuous.nse): callback = function(packetsz, layer2, layer3) return string.sub(layer2, 0, 12) -- 12 = 6B for dstmac + 6B srcmac end action = function(host, port) local pcap = nmap.new_socket() pcap:pcap_open(host.interface, 64, 0, callback, "arp") pcap:set_timeout(100) -- say that we're waiting for packet pcap:pcap_register(host.mac_addr_src .. host.mac_addr) -- concatenate mac's -- just wait for it status ,_,_,_ = pcap:pcap_receive() pcap:pcap_close() end