#!/usr/pkg/bin/ruby33

require 'icmp'

include ICMPModule
include Socket::Constants

host = ARGV.shift || 'localhost'
ai = Socket::getaddrinfo(host, nil, Socket::AF_INET)[0]
print "PING #{ai[2]} (#{ai[3]})\n"

# open ICMP socket.
sock = ICMPSocket.new
addr = ai[3].split(".").collect{|i| i.to_i }.pack("cccc")
sockaddr = make_sockaddr_in(AF_INET, 0, addr)

# make ICMP packet.
req = ICMP.new
req.icmp_type = ICMP_ECHO
req.icmp_code = 0
req.icmp_id = $$ & 0xffff

rtts = []
nsend = 0

trap("INT"){
  nrecv = rtts.size
  loss = (nsend - nrecv).to_f / nsend
  rtts.sort!
  min = rtts[0]
  max = rtts[-1]

  avg = 0.0
  rtts.each{|i| avg += i }
  avg /= rtts.size

  stddev = 0.0
  rtts.each{|i| stddev += i * i }
  stddev /= rtts.size
  stddev -= avg * avg

  form = "\n"
  form << "----#{ai[2]} PING Statistics----\n"
  form << "%d packets transmitted, %d packets received, %.1f%% packet loss\n"
  form << "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n"
  print form % [ nsend, nrecv, loss, min, avg, max, stddev ]
  exit
}

Thread.start do
  loop do
    buf = sock.recv(65535)
    recv_time = Time.now.to_f
    iph, repl = ICMPModule.split(buf)

    if repl.icmp_type == ICMP_ECHOREPLY && repl.icmp_id == req.icmp_id
      send_time = repl.icmp_data.unpack("d")[0]
      rtt = (recv_time - send_time) * 1000
      print "%d bytes from %s: icmp_seq=%d ttl=%d time=%.3f ms\n" %
	[repl.size, iph.ip_src, repl.icmp_seq, iph.ip_ttl, rtt]
      rtts.push rtt
    end
  end
end

1000.times { |i|
  req.icmp_seq = i
  req.icmp_data = [ Time.now.to_f ].pack("d")
  req.setup   # calc checksum and config packet length.
  sock.send(req, 0, sockaddr)
  nsend += 1
  sleep 1
}
