Cidr.rb: port of Perl's Net::CIDR v0.11 available

Module:

    http://www.catnook.com/programs/cidr.rb

Simple test program:

    http://www.catnook.com/programs/cidrtest.rb

Not sure about the license. Hope this is useful to someone.

As an aside: anyone have a port of Net::Ping they are willing to share? I'm
trying to convert some code (and colleagues!) to Ruby and am running into the
"missing libraries" issue...

···

--
Jos Backus
jos at catnook.com

Hi,

As an aside: anyone have a port of Net::Ping they are willing to share? I'm
trying to convert some code (and colleagues!) to Ruby and am running into the
"missing libraries" issue...

Check the RAA (and Rubyforge).

For ping, there is 'ping' in the standard library, and 'net/ping' on the
RAA ('gem install net-ping').

Pat Eyler wrote in May about pushing an ICMP-capable version of net/ping
into the standard library for 1.8.6.

Cheers,
Dave

Dave Burt wrote:

Hi,

> As an aside: anyone have a port of Net::Ping they are willing to share? I'm
> trying to convert some code (and colleagues!) to Ruby and am running into the
> "missing libraries" issue...

Check the RAA (and Rubyforge).

For ping, there is 'ping' in the standard library, and 'net/ping' on the
RAA ('gem install net-ping').

Pat Eyler wrote in May about pushing an ICMP-capable version of net/ping
into the standard library for 1.8.6.

Cheers,
Dave

Indeed, Pat talked to me about possibly including net-ping in the
standard lib, though it needs a PingICMP class. It could probably
stand a bit of reorganization, too.

In fact, I still have some code laying around that Sean Chittenden sent
to me a long time ago. Maybe it's time to revisit that. :slight_smile:

Regards,

Dan

Hi Dave,

Hi,

> As an aside: anyone have a port of Net::Ping they are willing to share? I'm
> trying to convert some code (and colleagues!) to Ruby and am running into the
> "missing libraries" issue...

Check the RAA (and Rubyforge).

For ping, there is 'ping' in the standard library, and 'net/ping' on the
RAA ('gem install net-ping').

I'm aware of those but they lack some features the Perl version has. Notably
ICMP support and returning timing information.

It would also be nice for there to be a single Net::Ping class, just like the
Perl version provides, wrapping/hiding the specialized classes. That seems
like an easy addition.

Pat Eyler wrote in May about pushing an ICMP-capable version of net/ping
into the standard library for 1.8.6.

/me crosses fingers...

Cheers,

···

On Wed, Nov 29, 2006 at 05:35:13PM +0900, Dave Burt wrote:
--
Jos Backus
jos at catnook.com

Dave Burt wrote:
> For ping, there is 'ping' in the standard library, and 'net/ping' on the
> RAA ('gem install net-ping').

ugh! I hate ping.

>
> Pat Eyler wrote in May about pushing an ICMP-capable version of net/ping
> into the standard library for 1.8.6.
>
> Cheers,
> Dave

Indeed, Pat talked to me about possibly including net-ping in the
standard lib, though it needs a PingICMP class. It could probably
stand a bit of reorganization, too.

I've actually done some work on the code, but it's in need of some
documentation and a bit more polish. The ICMP ping still needs
to be integrated as well.

I just have more ambition than time, talent, and energy. :frowning:

···

On 11/29/06, Daniel Berger <djberg96@gmail.com> wrote:

In fact, I still have some code laying around that Sean Chittenden sent
to me a long time ago. Maybe it's time to revisit that. :slight_smile:

Regards,

Dan

--
thanks,
-pate
-------------------------

Jos Backus wrote:

    Hi Dave,

Hi,

As an aside: anyone have a port of Net::Ping they are willing to share? I'm
trying to convert some code (and colleagues!) to Ruby and am running into the
"missing libraries" issue...
      

Check the RAA (and Rubyforge).

For ping, there is 'ping' in the standard library, and 'net/ping' on the
RAA ('gem install net-ping').
    
I'm aware of those but they lack some features the Perl version has. Notably
ICMP support and returning timing information.
  

Well, there is PingExternal for ICMP support. :wink:

Seriously, though, both would be good additions.

It would also be nice for there to be a single Net::Ping class, just like the
Perl version provides, wrapping/hiding the specialized classes. That seems
like an easy addition.
  

The API was modelled after the current ping.rb in the standard library to try to keep things consistant and more portable for anyone switching from ping.rb to my net-ping module. A factory pattern should be doable if that's what folks want. In practice, however, I don't see it as a major issue, and probably not worth the API breakage.

Regards,

Dan

···

On Wed, Nov 29, 2006 at 05:35:13PM +0900, Dave Burt wrote:

pat eyler wrote:

Dave Burt wrote:
> For ping, there is 'ping' in the standard library, and 'net/ping' on
the
> RAA ('gem install net-ping').

ugh! I hate ping.

Indeed, Pat talked to me about possibly including net-ping in the
standard lib, though it needs a PingICMP class. It could probably
stand a bit of reorganization, too.

I've actually done some work on the code, but it's in need of some
documentation and a bit more polish. The ICMP ping still needs
to be integrated as well.

I just have more ambition than time, talent, and energy. :frowning:

It sounds simple enough for a Ruby Quiz. Then we'd have not two, but a
dozen versions, possibly including a Net::Ping-interfaced one for Jos!

Dave

···

On 11/29/06, Daniel Berger <djberg96@gmail.com> wrote:

[snip]

>I'm aware of those but they lack some features the Perl version has.
>Notably
>ICMP support and returning timing information.
>
Well, there is PingExternal for ICMP support. :wink:

Seriously, though, both would be good additions.

Indeed.

>It would also be nice for there to be a single Net::Ping class, just like
>the
>Perl version provides, wrapping/hiding the specialized classes. That seems
>like an easy addition.
>
The API was modelled after the current ping.rb in the standard library
to try to keep things consistant and more portable for anyone switching
from ping.rb to my net-ping module. A factory pattern should be doable
if that's what folks want. In practice, however, I don't see it as a
major issue, and probably not worth the API breakage.

It eases the transition from Perl, as in my case. If you mean something like
the following then that would be great:

    lizzy:/tmp% cat r
    class Ping
      def self.create(type, *args)
  case type
  when :foo
    FooPing.new
  when :bar
    BarPing.new
  end
      end
    end

    class FooPing < Ping
      def ping(host)
  puts "#{self.class}: pinging #{host}"
      end
    end

    class BarPing < Ping
      def ping(host)
  puts "#{self.class}: pinging #{host}"
      end
    end

    pinger = Ping.create(:bar)
    pinger.ping("myhost")
    lizzy:/tmp% ruby r
    BarPing: pinging myhost
    lizzy:/tmp%

Let me know what I can do to help move this along.

···

On Fri, Dec 01, 2006 at 12:20:36AM +0900, Daniel Berger wrote:

--
Jos Backus
jos at catnook.com

I'm a huge fan of using the Ruby Quiz to improve libraries like this. I'm a Ping Dummy, so you guys put your heads together and write up something that explains the goals and if I can understand them, we've got a winner.

It takes a lot less time, talent, and energy to define the problem well enough to get help from the whole community and the results never cease to dazzle me...

James Edward Gray II

···

On Nov 29, 2006, at 3:25 PM, Dave Burt wrote:

pat eyler wrote:

Dave Burt wrote:

For ping, there is 'ping' in the standard library, and 'net/ping' on

the

RAA ('gem install net-ping').

ugh! I hate ping.

On 11/29/06, Daniel Berger <djberg96@gmail.com> wrote:

Indeed, Pat talked to me about possibly including net-ping in the
standard lib, though it needs a PingICMP class. It could probably
stand a bit of reorganization, too.

I've actually done some work on the code, but it's in need of some
documentation and a bit more polish. The ICMP ping still needs
to be integrated as well.

I just have more ambition than time, talent, and energy. :frowning:

It sounds simple enough for a Ruby Quiz. Then we'd have not two, but a
dozen versions, possibly including a Net::Ping-interfaced one for Jos!

Jos Backus wrote:

<snip>

r issue, and probably not worth the API breakage.
    
It eases the transition from Perl, as in my case. If you mean something like
the following then that would be great:

    lizzy:/tmp% cat r
    class Ping
      def self.create(type, *args)
  case type
  when :foo
    FooPing.new
  when :bar
    BarPing.new
  end
      end
    end

    class FooPing < Ping
      def ping(host)
  puts "#{self.class}: pinging #{host}"
      end
    end

    class BarPing < Ping
      def ping(host)
  puts "#{self.class}: pinging #{host}"
      end
    end

    pinger = Ping.create(:bar)
    pinger.ping("myhost")
    lizzy:/tmp% ruby r
    BarPing: pinging myhost
    lizzy:/tmp%

Let me know what I can do to help move this along.

Well, you could start by porting the ping_icmp() function from Ping.pm to Ruby. I started working on it but I got stuck and I need to work on other things. Below is what I've got so far:

require 'net/ping'

module Net
   class Ping::ICMP < Ping

      ICMP_ECHOREPLY = 0
      ICMP_ECHO = 8
      ICMP_SUBCODE = 0
      ICMP_FLAGS = 0
      ICMP_PORT = 0

      def initialize(*args)
         super(args)
         raise 'requires root privileges' if Process.euid > 0 # unsure
         @seq = 0
         @data_size = 0
         @data = ""
      end

      def checksum(msg)
         length = msg.length
         num_short = length / 2
         check = 0

         msg.unpack("n#{num_short}").each do |short|
            check += short
         end

         if length % 2 > 0
            check += msg[length-1, 1].unpack('C') << 8
         end

         check = (check >> 16) + (check & 0xffff)
         return (~((check >> 16) + check) & 0xffff)
      end

      def ping
         socket = Socket.new(
            Socket::PF_INET,
            Socket::SOCK_RAW,
            Socket::IPPROTO_ICMP
         )

         @seq = (@seq + 1) % 65536
         checksum = 0

         pstring = "C2 n3 A" << @data_size.to_s
         pid = Process.pid

         msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, pid, @seq, @data].pack(pstring)
         # TODO: finish
      end
   end
end

Regards,

Dan

I've made some progress; see attached. Let me know what you think.

ping_icmp.rb (2.75 KB)

···

On Fri, Dec 01, 2006 at 04:47:05AM +0900, Daniel Berger wrote:

Well, you could start by porting the ping_icmp() function from Ping.pm
to Ruby. I started working on it but I got stuck and I need to work on
other things. Below is what I've got so far:

--
Jos Backus
jos at catnook.com

Jos Backus wrote:

···

On Fri, Dec 01, 2006 at 04:47:05AM +0900, Daniel Berger wrote:

Well, you could start by porting the ping_icmp() function from Ping.pm to Ruby. I started working on it but I got stuck and I need to work on other things. Below is what I've got so far:

I've made some progress; see attached. Let me know what you think.

------------------------------------------------------------------------

#!/usr/bin/env ruby

require 'rubygems'
require 'net/ping'

module Net
  class Ping::ICMP < Ping

    ICMP_ECHOREPLY = 0
    ICMP_ECHO = 8
    ICMP_STRUCT = 'C2 n3 A'
    ICMP_SUBCODE = 0
    ICMP_FLAGS = 0
    ICMP_PORT = 0

    def initialize(*args)
      super(args)
      raise 'requires root privileges' if Process.euid > 0 # unsure
      @seq = 0
      @data_size = 0
      @data = ''
      @pid = Process.pid & 0xffff
    end

    def checksum(msg)
      length = msg.length
      num_short = length / 2
      check = 0

      msg.unpack("n#{num_short}").each do |short|
        check += short
      end

      if length % 2 > 0
        check += msg[length-1, 1].unpack('C') << 8
      end

      check = (check >> 16) + (check & 0xffff)
      return (~((check >> 16) + check) & 0xffff)
    end

    def ping(ip, timeout)
      ret = nil
      socket = Socket.new(
        Socket::PF_INET,
        Socket::SOCK_RAW,
        Socket::IPPROTO_ICMP
      )

      @seq = (@seq + 1) % 65536
      pstring = 'C2 n3 A' << @data_size.to_s

      checksum = 0
      msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
      checksum = checksum(msg)
      msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)

      begin
      saddr = Socket.pack_sockaddr_in(ICMP_PORT, ip)
      rescue => exc
  return nil
      end

      @from_ip = @from_type = @from_subcode = nil
      socket.send(msg, ICMP_FLAGS, saddr) # Send the message

      done = false
      finish_time = Time.now + timeout
      while !done and timeout > 0
        nfound = select([socket], nil, nil, timeout)
  timeout = finish_time - Time.now
        if nfound.nil? # timed out
          ret = nil
          done = true
        elsif !nfound[0].empty?
          recv_msg = ''
          from_pid = -1
          from_seq = -1
          recv_msg, from_saddr = socket.recvfrom(1500, ICMP_FLAGS)
          from_port, from_ip = Socket.unpack_sockaddr_in(from_saddr)
          from_type, from_subcode = recv_msg[20, 2].unpack('C2')
          case from_type
          when ICMP_ECHOREPLY
            if recv_msg.length >= 28
              from_pid, from_seq = recv_msg[24, 4].unpack('n3')
            end
          else
            if recv_msg.length >= 56
              from_pid, from_seq = recv_msg[52, 4].unpack('n3')
            end
          end
          @from_ip = from_ip
          @from_type = from_type
          @from_subcode = from_subcode
          if from_pid == @pid and from_seq == @seq
            if from_type == ICMP_ECHOREPLY
              ret = 1
            end
            done = true
          end
        else
    # Shouldn't happen
          done = true
        end
      end
      return ret
    end

  end

end

p = Net::Ping::ICMP.new
p p.ping(ARGV.shift, 5)

Seems to work. Mind if I include with the next release of net-ping (with acknowledgements, of course).

The only tweak I would make are to have it return true or false, instead of 1 or nil.

Regards,

Dan

Jos Backus wrote:
Seems to work. Mind if I include with the next release of net-ping
(with acknowledgements, of course).

Of course, thanks Daniel.

The only tweak I would make are to have it return true or false, instead
of 1 or nil.

Good idea. Now, the Perl Net::Ping library returns duration:

    ($ret, $duration, $ip) = $p->ping($host, $timeout);

    In array context, the elapsed time as well as the string form of the ip
    the host resolved to are also returned.

What would be the best way to add that to the Ruby version? Obviously one
would want all Net::Ping classes to support this. I have a local utility that
uses this information, which is why I need it.

Cheers,

···

On Sat, Dec 02, 2006 at 04:54:49AM +0900, Daniel Berger wrote:
--
Jos Backus
jos at catnook.com

Jos Backus wrote:

Jos Backus wrote:
Seems to work. Mind if I include with the next release of net-ping (with acknowledgements, of course).

Of course, thanks Daniel.

Great, thanks.

The only tweak I would make are to have it return true or false, instead of 1 or nil.

Good idea. Now, the Perl Net::Ping library returns duration:

    ($ret, $duration, $ip) = $p->ping($host, $timeout);

    In array context, the elapsed time as well as the string form of the ip
    the host resolved to are also returned.

What would be the best way to add that to the Ruby version? Obviously one
would want all Net::Ping classes to support this. I have a local utility that
uses this information, which is why I need it.

Cheers,

Hm, I'll have to think about the API. I suppose the most simple approach is to just add accessors for duration and ip_address (with a 'nil' value by default). In practice, then, your code would look like this:

if p.ping?
    puts p.ip_address
    puts p.duration
end

How does that look?

Regards,

Dan

···

On Sat, Dec 02, 2006 at 04:54:49AM +0900, Daniel Berger wrote:

I like this much better than the positional API that Perl uses here ("Is
duration the 1st or 2nd return value?"). Plus it makes it easy to add futher
items down the road without breaking existing code. +1.

···

On Sat, Dec 02, 2006 at 06:03:59AM +0900, Daniel Berger wrote:

Hm, I'll have to think about the API. I suppose the most simple
approach is to just add accessors for duration and ip_address (with a
'nil' value by default). In practice, then, your code would look like this:

if p.ping?
   puts p.ip_address
   puts p.duration
end

How does that look?

--
Jos Backus
jos at catnook.com

Jos Backus wrote:

> Hm, I'll have to think about the API. I suppose the most simple
> approach is to just add accessors for duration and ip_address (with a
> 'nil' value by default). In practice, then, your code would look like this:
>
> if p.ping?
> puts p.ip_address
> puts p.duration
> end
>
> How does that look?

I like this much better than the positional API that Perl uses here ("Is
duration the 1st or 2nd return value?"). Plus it makes it easy to add futher
items down the road without breaking existing code. +1.

Ok, I'll add those. However, don't put too much stock in the duration.
Looking at the Perl code, it isn't much more than this:

start = Time.now
p.ping?
duration = Time.now - start

And you can get the ip via IPSocket.getaddress(host)

Regards,

Dan

···

On Sat, Dec 02, 2006 at 06:03:59AM +0900, Daniel Berger wrote:

Ok, I'll add those. However, don't put too much stock in the duration.
Looking at the Perl code, it isn't much more than this:

start = Time.now
p.ping?
duration = Time.now - start

True, but one would want the timing measurement to be done as close as
possible around the send and the recvfrom, eliminating the avoidable overhead
of the method call and setup inside it being included in the duration. Plus,
having .duration is convenient, no?

And you can get the ip via IPSocket.getaddress(host)

Agreed. That functionality doesn't belong in this module.

···

On Sat, Dec 02, 2006 at 08:50:12PM +0900, Daniel Berger wrote:

--
Jos Backus
jos at catnook.com