Scapy how get ping time?

user1789326 picture user1789326 · Oct 31, 2012 · Viewed 8k times · Source

I'm trying to write a scapy script which can make an average on the ping time, so I need to get the time elapsed between ICMP echo/reply packet sent and reply packet received. For now, I have this:

#! /usr/bin/env python
from scapy.all import *
from time import *

def QoS_ping(host, count=3):
  packet = Ether()/IP(dst=host)/ICMP()
  t=0.0
  for x in range(count):
      t1=time()
      ans=srp(packet,iface="eth0", verbose=0)
      t2=time()
      t+=t2-t1
  return (t/count)*1000

The problem is that using time() function doesn't rise a good result. For example, I find 134 ms on one domain, and with the ping system function on the same domain, I have found 30 ms (average of course).

My question is: Is there a way to get the exactly time elpased beetween sent packet and received packet by scapy? I don't want to use popen() function or other system call because I need scapy for futur packet management.

Answer

Mike Pennington picture Mike Pennington · Nov 1, 2012

Scapy is slow because it's pure python parsing the whole packet in user space... it is not all that unusual to hack around scapy's thoughput limitations.

Making an apples to apples comparison... I have a Xeon server with a direct gig ethernet pipe to the internet, but my traffic is very light on it. When I run a normal ping to the Cisco router it's attached to, I'm averaging about 60 microseconds each...

[mpenning@Bucksnort ~]$ ping -W 1 -c 3 192.0.2.1
PING 192.0.2.1 (192.0.2.6) 56(84) bytes of data.
64 bytes from 192.0.2.1: icmp_req=1 ttl=64 time=0.078 ms
64 bytes from 192.0.2.1: icmp_req=2 ttl=64 time=0.062 ms
64 bytes from 192.0.2.1: icmp_req=3 ttl=64 time=0.062 ms

--- 192.0.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.062/0.067/0.078/0.010 ms
[mpenning@Bucksnort ~]$

The same destination in scapy... also measured in milliseconds...

[mpenning@Bucksnort ~]$ sudo python new_ping_ip.py
Ping: 0.285587072372
Ping: 0.230889797211
Ping: 0.219928979874
AVERAGE 245.468616486
[mpenning@Bucksnort ~]$

Scapy's results are almost 4000 times larger than a baseline ping from the bash prompt (245.469 / 0.062)... I ran the cable myself, it is less than a ten foot cable to the Cisco router.

What can you do to get better results? As mentioned in comments, look at sent_time and time... Packet.time is populated before parsing... this is still slower than a ping from the shell, but might help with your desire to capture packets in scapy.

#! /usr/bin/env python
from scapy.all import *

def QoS_ping(host, count=3):
  packet = Ether()/IP(dst=host)/ICMP()
  t=0.0
  for x in range(count):
      ans,unans=srp(packet,iface="eth0", filter='icmp', verbose=0)
      rx = ans[0][1]
      tx = ans[0][0]
      delta = rx.time-tx.sent_time
      print "Ping:", delta
      t+=delta
  return (t/count)*1000

if __name__=="__main__":
    total = QoS_ping('192.0.2.1')
    print "TOTAL", total

Sample run...

[mpenning@Bucksnort ~]$ sudo python ping_ip.py
Ping: 0.000389099121094
Ping: 0.000531911849976
Ping: 0.000631093978882
TOTAL 0.51736831665
[mpenning@Bucksnort ~]$

Even using Packet.time and Packet.sent_time is slow compared to a shell call though...

>>> from subprocess import Popen, PIPE
>>> import re
>>> cmd = Popen('ping -q -c 3 192.0.2.1'.split(' '), stdout=PIPE)
>>> output = cmd.communicate()[0]
>>> match = re.search('(\d+\.\d+)\/(\d+\.\d+)\/(\d+\.\d+)\/(\d+\.\d+)\s+ms', output)
>>> if not (match is None):
...     print "Average %0.3f" % float(match.group(1))
... else:
...     print "Failure"
...
Average 0.073
>>>

ping -q -c 3 provides the summary output of 3 pings without the individual pings printed.

If you want to capture your ping packets (through a shell ping call) for later scapy processing, spawn tcpdump -c <num-packets> -w <filename> icmp and host <host-addr> & before running your CLI ping... then use scapy's rdpcap() to read the pcap file from tcpdump. Be sure to properly calculate the number of packets you will capture in your pcap file.