IP arithmetic

Are there any functions in the Ruby standard library which convert
dotted-decimal IP addresses to numeric form, without doing any forward or
reverse DNS lookups?

I want to do some address subnet matching: e.g.

if ip_match("1.2.3.4", "1.2.3.0/24")
  ... etc
end

and the efficient way of doing this would be to do bitwise operations on the
numeric form.

Cheers,

Brian.

Attached is something I’ve thrown together over the past year or so.

ruby -I. -rnetaddr -e 'p IP4NetAddr.new("128.227.0.0/16").contains("128.127.215.35")'

More can be done I’m sure. Feel free to add.

Jim

netaddr.rb.txt (3.15 KB)

···

On Wed, 2 Jul 2003 01:12:15 +0900 Brian Candler B.Candler@pobox.com wrote:

Are there any functions in the Ruby standard library which convert
dotted-decimal IP addresses to numeric form, without doing any forward or
reverse DNS lookups?

I want to do some address subnet matching: e.g.

if ip_match("1.2.3.4", "1.2.3.0/24")
  ... etc
end

and the efficient way of doing this would be to do bitwise operations on the
numeric form.

Saluton!

  • Brian Candler; 2003-07-02, 10:46 UTC:
if ip_match("1.2.3.4", "1.2.3.0/24")
  ... etc
end

and the efficient way of doing this would be to do bitwise operations on the
numeric form.

Like the following?

def ipmatch(ip, net)

unless ip =~ /^(((1?\d|2[0-4])?\d|25[0-5]).){3}((1?\d|2[0-4])?\d|25[0-5])$/ or
net =~ /^(((1?\d|2[0-4])?\d|25[0-5]).){3}((1?\d|2[0-4])?\d|25[0-5])/([12]?\d|3[0-2])$/
raise ArgumentError
end

/^(\d+).(\d+).(\d+).(\d+)$/.match(ip)
ipbin = $4.to_i + 256 * ($3.to_i + 256 * ($2.to_i + 256 * $1.to_i))
/(\d+).(\d+).(\d+).(\d+)/(\d+)/.match(net)
rbin = $4.to_i + 256 * ($3.to_i + 256 * ($2.to_i + 256 * $1.to_i))
s = 32 - $5.to_i
(ipbin ^ rbin) & (~0 >> s << s) == 0
end

Some comments: The lengthy regex ensure that the IP consists of four
numbers in the 0.255 range and that the net consists of the same
followed by a slash followed by a number in the range 0…32 where
zero is the whole IPv4 Internet.

The first match assigns each number to an individual string; the next
line convert’s form base 256 to base 10 using Horner’s scheme (quite
efficient).

Next two lines do the same for net but additionally assign the number
of bits to an additional variable.

~0 >> s << s

generates a number of all but the last s bits equal one.

The whole meaning of

(ipbin ^ rbin) & (~0 >> s << s) == 0

is:

Find all bits that ipbin and rbin differ in, ignore all that are
allowed to be different and see if there are any left.

Port from C :->

Gis,

Josef ‘Jupp’ Schugt

···


Someone even submitted a fingerprint for Debian Linux running on the
Microsoft Xbox. You have to love that irony :).
– Fyodor on nmap-hackers@insecure.org

Saluton!

  • Brian Candler; 2003-07-01, 16:12 UTC:
if ip_match("1.2.3.4", "1.2.3.0/24")
  ... etc
end

Found solution in Ruby 1.8 pre 3:

require ‘ipaddr’

def ip_match(ip, net)
IPAddr.new(net).include?(IPAddr.new(ip))
end

Cannot test it - ipaddr.rb requires IPv6 support which is not
available here.

Gis,

Josef ‘Jupp’ Schugt

···


Someone even submitted a fingerprint for Debian Linux running on the
Microsoft Xbox. You have to love that irony :).
– Fyodor on nmap-hackers@insecure.org

  • Brian Candler; 2003-07-02, 10:46 UTC:
if ip_match("1.2.3.4", "1.2.3.0/24")
  ... etc
end

and the efficient way of doing this would be to do bitwise operations on the
numeric form.

Like the following?

def ipmatch(ip, net)

Yeah, I ended up writing something like that. I just wondered if there was
any built-in library function which would do it.

~0 >> s << s

generates a number of all but the last s bits equal one.

That’s rather dodgy, notice that the first shift is unnecessary:

irb(main):002:0> ~0
=> -1
irb(main):003:0> ~0 >> 3
=> -1
irb(main):004:0> ~0 >> 3 << 3
=> -8

(I think you have been following C source code too closely, but even then
I’m not sure if the result of right-shifting a signed value is
implementation-dependent)

I decided to avoid negative numbers altogether, since Ruby is not C with its
32-bit wraparound, so I did

mask = (0xffffffff << s) & 0xffffffff

The whole meaning of

(ipbin ^ rbin) & (~0 >> s << s) == 0

is:

Find all bits that ipbin and rbin differ in, ignore all that are
allowed to be different and see if there are any left.

Or more simply:

(ipbin & mask) != rbin

Regards,

Brian.

···

On Thu, Jul 03, 2003 at 05:19:02AM +0900, Josef ‘Jupp’ Schugt wrote: