2

Problem

I have written a script that sniffs packet from a host, however, I am sniffing the packets in continuous mode and would like to stop sniffing on a timeout. I have written the following code to stop packet sniffing, but it doesn't seem to stop when the time has clearly exceeded the timeout. What could I be doing wrong in here?

import time
import pyshark

prog_start = time.time()
capture = pyshark.LiveCapture(interface='en0')
capture.sniff(timeout=10)
start_time = capture[0].frame_info.time_epoch
end_time = capture[-1].frame_info.time_epoch
print("Capture lasted:", float(end_time) - float(start_time))
pkt_num = 0
for pkt in capture:
    pkt_num += 1
    print("Time", time.time() - prog_start, "Pkt#", pkt_num)

We then get this output, with thousands of additional packets a second, past when the capture should have stopped:

Capture lasted: 9.148329019546509
Time 10.346031188964844 Pkt# 1
Time 10.348641157150269 Pkt# 2
Time 10.351708889007568 Pkt# 3
Time 10.353564977645874 Pkt# 4
Time 10.35555100440979 Pkt# 5
...

Question

Why does PyShark continue to capture packets after the timeout?

Ross Jacobs
  • 2,962
  • 1
  • 17
  • 27
Sajan Maharjan
  • 118
  • 1
  • 10
  • Can you reduce the code here to an [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)? – Ross Jacobs Oct 10 '19 at 02:43
  • `capture = pyshark.LiveCapture(interface='\\Device\\NPF_{9342EE7E-9981-4554-87AE-06666A717864}', display_filter='bitcoin')` `capture.sniff(timeout=125)` `start_time = capture[0].frame_info.time_epoch` `end_time = capture[-1].frame_info.time_epoch` `for pkt in capture:` `print(str(pkt))` When I use timeout as you mentioned, it is continuously looping to even after timeout. – Sajan Maharjan Oct 10 '19 at 04:21
  • Updated question and answer - it looks like a bug – Ross Jacobs Oct 10 '19 at 05:11

3 Answers3

2

I was having this same issue, I managed to find a bit of a solution for it. It isn't perfect but it works by telling the capture loop to stop on the next packet and sends an empty packet it will see to make it end. I made it a udp packet on a high port for my case because I use a filter that filters out most traffic so this solution worked for me

class PacketCapture(threading.Thread):
    capture = 1

    def __init__(self, interface_name):
        threading.Thread.__init__(self)
        self.interface_name = interface_name

    def stop(self):
        self.capture = 0

    def run(self):
        capture = pyshark.LiveCapture(interface=self.interface_name)
        try:
            for packet in capture.sniff_continuously():
                if not self.capture:
                    capture.close()
        except pyshark.capture.capture.TSharkCrashException:
            self.exited = 1
            print("Capture has crashed")



#start capture
pcap = PacketCapture(interface_name)
pcap.start()

#stop capture
pcap.stop()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
msg = bytes("", "UTF-8")
sock.sendto(msg, ("external IP", 12345))
sock.close

I'm relatively new to python myself but I think this should be a somewhat acceptable solution given the scenario

Desultory
  • 53
  • 1
  • 11
1

Problems with PyShark

It looks like you're running into a known issue with PyShark that hasn't been fixed in years. Per the thread, the author wrote

You can subclass LiveCapture and override the get_parameters() function, adding your own parameters.

You could modify the parameters sent to tshark, but at this point, why not just use a tshark command directly?

Using Tshark Instead

PyShark is just a wrapper for tshark on your system. If you want to use subprocess with Python, the equivalent tshark command is tshark -a duration:5. The other advantage of using tshark directly is that subprocess gives you a pid that you can kill on an arbitrary condition.

See the manpage for more details.

Ross Jacobs
  • 2,962
  • 1
  • 17
  • 27
  • Thank you but I still can't get it right. I mean if I use timeout option to set timeout and capture as many packets as I like, but when I have to loop through the captured packets again using for loop (like in my code above), it goes into async mode and then captures new packets again. So I am still stuck. Any alternatives you could suggest? – Sajan Maharjan Oct 10 '19 at 02:31
1

You can use the following line in bash or even in python within os.system, os.popen or even subprocess:

while IFS= read -r line; do if [[ $line =~ 'some protocol' ]]; then <SOME_ACTION>; break; fi; done < <(sudo tshark)
Maf
  • 696
  • 1
  • 8
  • 23