def recvPackets(self,pingJob): """receive a packet and decode its header""" while time.time() - pingJob.start<self.timeout: rl,wl,el = select.select([self.pingsocket],[],[],0) if self.pingsocket in rl: try: data, (host, port) = self.pingsocket.recvfrom(1024) if not data: return ipreply = ip.disassemble(data) try: icmppkt = icmp.disassemble(ipreply.data) except ValueError: print ("checksum failure on packet %r", ipreply.data) try: icmppkt = icmp.disassemble(ipreply.data, 0) except ValueError: continue # probably Unknown type except Exception, ex: print ("Unable to decode reply packet payload %s", ex) continue sip = ipreply.src if (icmppkt.get_type() == icmp.ICMP_ECHOREPLY and icmppkt.get_id() == self.procId and sip == pingJob.ipaddr): pingJob.rrt = (time.time()-pingJob.start)*1000 if pingJob.rrt < 1: time_print = "time<1ms" else: time_print = "time=%dms"%(pingJob.rrt) print "Reply from %s: bytes=%s %s TTL=%d"%(sip,ipreply.len,time_print,ipreply.ttl) pingJob.pingSuc() elif icmppkt.get_type() == icmp.ICMP_UNREACH: try: origpkt = icmppkt.get_embedded_ip() dip = origpkt.dst if (origpkt.data.find(self.pktdata) > -1 and dip == pingJob.ipaddr): print ("pj fail for %s", pingJob.ipaddr) except ValueError, ex: print ("failed to parse host unreachable packet") else: pingJob.message = "unexpected pkt %s %s"% (sip, icmppkt) continue break except (SystemExit, KeyboardInterrupt): raise except socket.error, err: errnum, errmsg = err.args if errnum == errno.EAGAIN: return raise err
def wait(self): start = time.time() timeout = 0.05 while 1: rd, wt, er = select.select([self.sock.socket], [], [], timeout) if rd: # okay to use time here, because select has told us # there is data and we don't care to measure the time # it takes the system to give us the packet. arrival = time.time() try: pkt, who = self.sock.recvfrom(4096) except socket.error: continue # could also use the ip module to get the payload repip = ip.disassemble(pkt) try: reply = icmp.disassemble(repip.data) except ValueError: continue try: if reply.get_id() == self.ping_ident: self.recv_packet(reply, arrival) self.last_arrival = arrival except AttributeError, e: if reply.get_embedded_ip().dst == self.addr: raise exceptions.DoesntExistException('host %s ' 'unreachable (%s)' % (self.addr, e)) # else we are receiving a hostunreach for another host, # just ignore it and continue waiting. timeout = (start + 0.05) - time.time() if timeout < 0: break
def wait(self): start = time.time() timeout = 1.0 while 1: rd, wt, er = select.select([self.sock.socket], [], [], timeout) if rd: # okay to use time here, because select has told us # there is data and we don't care to measure the time # it takes the system to give us the packet. arrival = time.time() try: pkt, who = self.sock.recvfrom(4096) except socket.error: continue # could also use the ip module to get the payload repip = ip.disassemble(pkt) try: reply = icmp.disassemble(repip.data) except ValueError: continue if reply.get_id() == self.pid: self.recv_packet(reply, arrival) self.last_arrival = arrival timeout = (start + 1.0) - time.time() if timeout < 0: break
def testUdpPortWronlyParsed(self): ip_reply = ip.disassemble(self.pkt) icmp_reply = icmp.disassemble(ip_reply.data) #ICMP is payload of IP inner_ip = icmp_reply.get_embedded_ip() self.assertEqual('10.249.64.152', inner_ip.src) self.assertEqual('207.68.173.76', inner_ip.dst) udp_in_icmp = udp.disassemble(inner_ip.data, 0) self.assertEqual(40164, udp_in_icmp.sport) self.assertEqual(33435, udp_in_icmp.dport)
def loop(hosts, callback, sleep_interval = 1, timeout_interval = 1): sendq = [] recvq = {} jitter = 0 vario = 0.1 for host in hosts: heappush(sendq, (time.time()+sleep_interval+jitter, host)) jitter += vario psocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp')) psocket.setblocking(0) poller = select.poll() poller.register(psocket.fileno(), select.POLLOUT | select.POLLIN) my_id = os.getpid() seq = 0 while True: events = poller.poll(1) current_time = time.time() for fileno, event in events: if event & select.POLLIN: #curent_time = time.time() (data, addr) = psocket.recvfrom(2048) (r_id, r_seq, time_sent) = icmp.disassemble(data) if r_id: rtt = (current_time - time_sent) * 1000 if r_id == my_id: callback(addr[0], rtt) heappush(sendq, (time.time() + sleep_interval, addr[0])) if addr[0] in recvq: del recvq[addr[0]] if event & select.POLLOUT: if sendq: (time_to_send, ip) = heappop(sendq) if time_to_send < current_time: icmp_packet = icmp.assemble(ip, my_id, seq, current_time) seq += 1 psocket.sendto(icmp_packet, (ip,0)) recvq[ip] = current_time + timeout_interval else: heappush(sendq, (time_to_send, ip)) #TODO optimize here for ip in [ host for (host, timeout) in recvq.items() if timeout < current_time ]: callback(ip, None) heappush(sendq, (current_time + sleep_interval, ip)) del recvq[ip] if seq > 10000: seq=0
def received(self, pkt, gateway): """ Upon received an incoming ICMP, determine if the packet is of our interest If it is, populate related fields, and return 1 otherwise return 0 """ ip_reply = ip.disassemble(pkt) if icmp.HDR_SIZE_IN_BYTES > len(ip_reply.data): return 0 #IP payload is not long enough to hold ICMP try: icmp_reply = icmp.disassemble(ip_reply.data) #ICMP is payload of IP except ValueError, msg: if DEBUG: stdout.write("Bad Packet received!") return 0
def received(self, pkt, gateway): """ Upon received an incoming ICMP, determine if the packet is of our interest If it is, populate related fields, and return 1 otherwise return 0 """ ip_reply = ip.disassemble(pkt) if icmp.HDR_SIZE_IN_BYTES > len(ip_reply.data): return 0 #IP payload is not long enough to hold ICMP try: icmp_reply = icmp.disassemble( ip_reply.data) #ICMP is payload of IP except ValueError, msg: if DEBUG: stdout.write("Bad Packet received!") return 0
def received(self, pkt, gateway): """ Upon received an incoming ICMP, determine if the packet is of our interest If it is, populate related fields, and return 1 otherwise return 0 """ ip_reply = ip.disassemble(pkt) if icmp.HDR_SIZE_IN_BYTES > len(ip_reply.data): return 0 #IP payload is not long enough to hold ICMP icmp_reply = icmp.disassemble(ip_reply.data) #ICMP is payload of IP if DEBUG: stdout.write( "recvfrom %s: ICMP_type: %d ICMP_code: %d" % (gateway, icmp_reply.get_type(), icmp_reply.get_code())) # 2 conditions interest us: # 1. ICMP_TIMEEXCEED, probe packet dropped by gateway in the middle, # which then send ICMP_TIMEEXCEED back to us # 2. ICMP_ECHOREPLY, probe packet reach destination host, if (icmp_reply.get_type() == icmp.ICMP_TIMXCEED \ and icmp_reply.get_code() == icmp.ICMP_TIMXCEED_INTRANS) \ or icmp_reply.get_type() == icmp.ICMP_ECHOREPLY: if ip.MIN_HDR_SIZE_IN_BYTES > len(icmp_reply.data): return 0 #ICMP payload is not long enough for inner IP inner_ip = ip.disassemble(icmp_reply.data) if inner_ip.src != self.src_addr or inner_ip.dst != self.dst_addr: return 0 self.gateway = gateway self.timestamp_received = time.time() if icmp_reply.get_type() == icmp.ICMP_ECHOREPLY: self.dst_reached = 1 return 1 if self.verbose: # In verbose mode, ICMP packets other than IME_EXCEEDED and UNREACHABLE are listed stdout.write( "recvfrom %s: ICMP_type: %d ICMP_code: %d" % (gateway, icmp_reply.get_type(), icmp_reply.get_code())) return 0
def received(self, pkt, gateway): """ Upon received an incoming ICMP, determine if the packet is of our interest If it is, populate related fields, and return 1 otherwise return 0 """ ip_reply = ip.disassemble(pkt) if icmp.HDR_SIZE_IN_BYTES > len(ip_reply.data): return 0 #IP payload is not long enough to hold ICMP icmp_reply = icmp.disassemble(ip_reply.data) #ICMP is payload of IP if DEBUG: stdout.write("recvfrom %s: ICMP_type: %d ICMP_code: %d" % (gateway, icmp_reply.get_type(), icmp_reply.get_code())) # 2 conditions interest us: # 1. ICMP_TIMEEXCEED, probe packet dropped by gateway in the middle, # which then send ICMP_TIMEEXCEED back to us # 2. ICMP_ECHOREPLY, probe packet reach destination host, if (icmp_reply.get_type() == icmp.ICMP_TIMXCEED \ and icmp_reply.get_code() == icmp.ICMP_TIMXCEED_INTRANS) \ or icmp_reply.get_type() == icmp.ICMP_ECHOREPLY: if ip.MIN_HDR_SIZE_IN_BYTES > len(icmp_reply.data): return 0 #ICMP payload is not long enough for inner IP inner_ip = ip.disassemble(icmp_reply.data) if inner_ip.src != self.src_addr or inner_ip.dst != self.dst_addr: return 0 self.gateway = gateway self.timestamp_received = time.time() if icmp_reply.get_type() == icmp.ICMP_ECHOREPLY: self.dst_reached = 1 return 1 if self.verbose: # In verbose mode, ICMP packets other than IME_EXCEEDED and UNREACHABLE are listed stdout.write("recvfrom %s: ICMP_type: %d ICMP_code: %d" % (gateway, icmp_reply.get_type(), icmp_reply.get_code())) return 0
def ping(self, packets=1): """ Send ping messages, as many as given with packets parameter. Returns a dictionary of results, may raise PingError. """ try: packets = int(packets) if packets < 0 or packets > MAX_PACKETS: raise ValueError except ValueError: raise PingError('Invalid number of packets: %s' % packets) interval = float(self.interval)/1000 timeout = float(self.timeout)/1000 last_sent = 0 while 1: now = time.time() if self.sent < packets and (last_sent+interval)<now: self.__send_packet() last_sent = now if len(self.times) == packets: if filter(lambda t: t+timeout>now, self.times.values())==[]: break (rd, wt, er) = select.select([self.socket.socket], [], [], timeout) if not rd: continue arrival = time.time() try: (pkt, who) = self.socket.recvfrom(4096) except socket.error: continue reply_address = ip.disassemble(pkt) try: reply = icmp.disassemble(reply_address.data) except ValueError: print 'Invalid ICMP reply packet received' continue if reply.get_id() != self.pid: print 'PID in response does not match' continue self.__recv_packet(reply, arrival, timeout) if self.sent < packets: continue if len(self.deltas)+len(self.timed_out) == packets: break received = len(self.deltas) loss = (float(packets-received)/float(packets))*100 if len(self.deltas) == 0: summary = { 'min': None, 'max': None, 'average': None, 'sent': packets, 'received': len(self.deltas) + len(self.timed_out), 'packetloss': loss } else: summary = { 'min': min(self.deltas), 'max': max(self.deltas), 'average': reduce(lambda x, y: x+y, self.deltas) / len(self.deltas), 'sent': packets, 'received': received, 'packetloss': loss } self.__reset_counters() return summary
def testTimeExceeded(self): buf = icmp.assemble(self.time_exceeded, cksum=1) new = icmp.disassemble(buf, cksum=1) self.assertEqual(self.time_exceeded, new)
def testUnreachableNotCksum(self): buf = icmp.assemble(self.unreachable, cksum=0) new = icmp.disassemble(buf, cksum=0) self.assertEqual(self.unreachable, new)
def testEchoReplyNotCksum(self): buf = icmp.assemble(self.echo_reply, cksum=0) new = icmp.disassemble(buf, cksum=0) self.assertEqual(self.echo_reply, new)
def testEcho(self): buf = icmp.assemble(self.echo, cksum=1) new = icmp.disassemble(buf, cksum=1) self.assertEqual(self.echo, new)
def ping(self, packets=1): """ Send ping messages, as many as given with packets parameter. Returns a dictionary of results, may raise PingError. """ try: packets = int(packets) if packets < 0 or packets > MAX_PACKETS: raise ValueError except ValueError: raise PingError('Invalid number of packets: %s' % packets) interval = float(self.interval) / 1000 timeout = float(self.timeout) / 1000 last_sent = 0 while 1: now = time.time() if self.sent < packets and (last_sent + interval) < now: self.__send_packet() last_sent = now if len(self.times) == packets: if filter(lambda t: t + timeout > now, self.times.values()) == []: break (rd, wt, er) = select.select([self.socket.socket], [], [], timeout) if not rd: continue arrival = time.time() try: (pkt, who) = self.socket.recvfrom(4096) except socket.error: continue reply_address = ip.disassemble(pkt) try: reply = icmp.disassemble(reply_address.data) except ValueError: print 'Invalid ICMP reply packet received' continue if reply.get_id() != self.pid: print 'PID in response does not match' continue self.__recv_packet(reply, arrival, timeout) if self.sent < packets: continue if len(self.deltas) + len(self.timed_out) == packets: break received = len(self.deltas) loss = (float(packets - received) / float(packets)) * 100 if len(self.deltas) == 0: summary = { 'min': None, 'max': None, 'average': None, 'sent': packets, 'received': len(self.deltas) + len(self.timed_out), 'packetloss': loss } else: summary = { 'min': min(self.deltas), 'max': max(self.deltas), 'average': reduce(lambda x, y: x + y, self.deltas) / len(self.deltas), 'sent': packets, 'received': received, 'packetloss': loss } self.__reset_counters() return summary