Code Monkey home page Code Monkey logo

ipaddress's Introduction

IPAddress

IPAddress is a Ruby library designed to make the use of IPv4 and IPv6 addresses simple, powerful and enjoyable. It provides a complete set of methods to handle IP addresses for any need, from simple scripting to full network design.

This document provides a brief introduction to the library and examples of typical usage.

Requirements

  • Ruby 1.9.3 or later

Please refer to Travis CI for Build Tests on specific versions of Ruby.

Build Status Dependency Status

IPAddress 0.8.2 was manually tested on:

  • ruby-1.8.7-p334 [ i386 ]
  • ree-1.8.7-2011.03 [ i386 ]
  • rbx-head [ ]
  • jruby-1.6.1 [ linux-i386-java ]
  • ruby-1.9.1-p431 [ i386 ]
  • ruby-1.9.2-p180 [ i386 ]
  • ruby-2.0.0-p353 [ x86_64-darwin14.0.0 ]
  • ruby-2.1.3-p242 [ x86_64-darwin14.0.0 ]

If you want to contribute, please refer to [Contributing.md](https://github.com/ipaddress-gem/ipaddress/blob/master/CONTR IBUTING.md).

Installation

This gem is installed like any other:

# Install it generally:
$ gem install ipaddress

# or, add it to your current application gemfile:
$ bundle add ipaddress

Documentation

The code is fully documented with RDoc. You can generate the documentation with Rake:

$ rake rdoc

The latest documentation can be found online at [this address][https://rubydoc.info/gems/ipaddress/]

Introduction via examples

Below are two sections with examples. The first section is for IPv4, the second for IPv6 (further down).

IPv4

The class IPAddress::IPv4 is used to handle IPv4 type addresses.

Create a new IPv4 address

The usual way to express an IP Address is using its dotted decimal form, such as 172.16.10.1, and a prefix, such as 24, separated by a slash.

172.16.10.1/24

To create a new IPv4 object, you can use IPv4 own class

ip = IPAddress::IPv4.new "172.16.10.1/24"

or, in a easier way, using the IPAddress parse method

ip = IPAddress.parse "172.16.10.1/24"

which accepts and parses any kind of IP (uint32, IPv4, IPV6 and IPv4 IPv6 mapped addresses).

If you like syntactic sugar, you can use the wrapper method IPAddress(), which is built around IPAddress::parse:

ip = IPAddress "172.16.10.1/24"

You can specify an IPv4 address in any of two ways:

IPAddress "172.16.10.1/24"
IPAddress "172.16.10.1/255.255.255.0"

In this example, prefix /24 and netmask 255.255.255.0 are the same and you have the flexibility to use either one of them.

If you don't explicitly specify the prefix (or the subnet mask), IPAddress thinks you're dealing with host addresses and not with networks. Therefore, the default prefix will be /32, or 255.255.255.255. For example:

# let's declare an host address
host = IPAddress::IPv4.new "10.1.1.1"

puts host.to_s #=> "10.1.1.1/32"

The new created object has prefix /32, which is the same as we created the following:

host = IPAddress::IPv4.new "10.1.1.1/32"

You can also pass a uint32 to obtain an IPAddress::IPv4 object:

# Create host object
ip = IPAddress 167837953
puts ip.to_s #=> "10.1.1.1/32"

Handling the IPv4 address

Once created, you can obtain the attributes for an IPv4 object:

ip = IPAddress("172.16.10.1/24")

ip.address #=> "172.16.10.1"
ip.prefix #=> 24

In case you need to retrieve the netmask in IPv4 format, you can use the IPv4#netmask method:

ip.netmask #=> "255.255.255.0"

A special attribute, IPv4#octets, is available to get the four decimal octets from the IP address:

ip.octets #=> [172,16,10,1]

The shortcut method IPv4#[], provides access to a given octet whithin the range:

ip[1] #=> 16

If you need to print out the IPv4 address in a canonical form, you can use IPv4#to_s:

ip.to_s #=> "172.16.10.1/24"

Changing netmask

You can set a new prefix (netmask) after creating an IPv4 object. For example:

ip.prefix = 25

ip.to_s #=> "172.16.10.1/25"

If you need to use a netmask in IPv4 format, you can achive so by using the IPv4#netmask= method:

ip.netmask = "255.255.255.252"

ip.to_s #=> "172.16.10.1/30"

Working with networks, broadcasts and addresses

Some important topics in dealing with IP addresses are the concepts of network and broadcast, as well as the addresses included in a range.

When you specify an IPv4 address such as 172.16.10.1/24, you are actually handling two different information:

  • The IP address itself, "172.16.10.1"
  • The subnet mask which indicates the network

The network number is the IP which has all zeroes in the host portion. In our example, because the prefix is 24, we identify our network number to have the last 8 (32-24) bits all zeroes. Thus, IP address 172.16.10.1/24 belongs to network 172.16.10.0/24.

This is important because, for instance, IP 172.16.10.1/16 is different to the previous one, belonging to the different network 172.16.0.0/16.

Networks

With IPAddress it's easy to calculate the network for an IP address:

ip = IPAddress "172.16.10.1/24"

net = ip.network #=> #<IPAddress::IPv4:0xb7a5ab24 @octets=[172, 16, 10, 0], @prefix=24, @address="172.16.10.0">
net.to_s #=> "172.16.10.0/24"

Method IPv4#network creates a new IPv4 object from the network number, calculated after the original object. We want to outline here that the network address is a perfect legitimate IPv4 address, which just happen to have all zeroes in the host portion.

You can use method IPv4#network? to check whether an IP address is a network or not:

ip1 = IPAddress "172.16.10.1/24"
ip2 = IPAddress "172.16.10.4/30"

ip1.network? #=> false
ip2.network? #=> true

Broadcast

The broadcast address is the contrary than the network number: where the network number has all zeroes in the host portion, the broadcast address has all one's. For example, ip 172.16.10.1/24 has broadcast 172.16.10.255/24, where ip 172.16.10.1/16 has broadcast 172.16.255.255/16.

Method IPv4#broadcast has the same behavior as is #network counterpart: it creates a new IPv4 object to handle the broadcast address:

ip = IPAddress "172.16.10.1/24"

bcast = ip.broadcast #=> #<IPAddress::IPv4:0xb7a406fc @octets=[172, 16, 10, 255], @prefix=24, @address="172.16.10.255">
bcast.to_s #=> "172.16.10.255/24"

Addresses, ranges and iterators

So we see that the netmask essentially specifies a range for IP addresses that are included in a network: all the addresses between the network number and the broadcast. IPAddress has many methods to iterate between those addresses. Let's start with IPv4#each, which iterates over all addresses in a range

ip = IPAddress "172.16.10.1/24"

ip.each do |addr|
  puts addr
end

It is important to note that it doesn't matter if the original IP is a host IP or a network number (or a broadcast address): the #each method only considers the range that the original IP specifies.

If you only want to iterate over hosts IP, use the IPv4#each_host method:

ip = IPAddress "172.16.10.1/24"

ip.each_host do |host|
  puts host
end

Methods IPv4#first and IPv4#last return a new object containing respectively the first and the last host address in the range

ip = IPAddress "172.16.10.100/24"

ip.first.to_s #=> "172.16.10.1/24"
ip.last.to_s #=> "172.16.10.254/24"

Checking if an address is loopback is easy with the IPv4#loopback? method:

ip = IPAddress "127.0.0.1"

ip.loopback? #=> true

Checking if an address is in the multicast range can be done using the IPv4#multicast? method:

ip = IPAddress "224.0.0.1/32"

ip.multicast? #=> true

The ability to generate a range also exists by using the IPv4#to() method. This allows you to create a subnet agnostic range based off a fixed amount.

ip = IPAddress "172.16.10.100/24"
ip.to('172.16.10.110') #=> ["172.16.10.100", ..., "172.16.10.110"]

IP special formats

The IPAddress library provides a complete set of methods to access an IPv4 address in special formats, such as binary, 32 bits unsigned int, data and hexadecimal.

Let's take the following IPv4 as an example:

ip = IPAddress "172.16.10.1/24"

ip.address #=> "172.16.10.1"

The first thing to highlight here is that all these conversion methods only take into consideration the address portion of an IPv4 object and not the prefix (netmask).

So, to express the address in binary format, use the IPv4#bits method:

ip.bits #=> "10101100000100000000101000000001"

To calculate the 32 bits unsigned int format of the ip address, use the IPv4#to_u32 method

ip.to_u32 #=> 2886732289

This method is the equivalent of the Unix call pton(), expressing an IP address in the so called +network byte order+ notation. However, if you want to transmit your IP over a network socket, you might need to transform it in data format using the IPv4#data method:

ip.data #=> "\254\020\n\001"

Also, you can transform an IPv4 address into a format which is suitable to use in IPv4-IPv6 mapped addresses:

ip.to_ipv6 #=> "ac10:0a01"

Finally, much like IPv4#to_ipv6 you can use to IPv4#to_h method to return a non-semicolon delineated string (useful with pcap/byte level usage):

ip.to_h #=> "ac100a01"

Classful networks

IPAddress allows you to create and manipulate objects using the old and deprecated (but apparently still popular) classful networks concept.

Classful networks and addresses don't have a prefix: their subnet mask is univocally identified by their address, and therefore divided in classes. As per RFC 791, these classes are:

  • Class A, from 0.0.0.0 to 127.255.255.255
  • Class B, from 128.0.0.0 to 191.255.255.255
  • Class C, from 192.0.0.0 to 255.255.255.255

Since classful networks here are only considered to calculate the default prefix number, classes D and E are not considered.

To create a classful IP and prefix from an IP address, use the IPv4::parse_classful method:

# classful ip
ip = IPAddress::IPv4::parse_classful "10.1.1.1"

ip.prefix #=> 8

The method automatically created a new IPv4 object and assigned it the correct prefix.

You can easily check which classful network an IPv4 object belongs:

ip = IPAddress("10.0.0.1/24")
ip.a? #=> true

ip = IPAddress("172.16.10.1/24")
ip.b? #=> true

ip = IPAddress("192.168.1.1/30")
ip.c? #=> true

Remember that these methods are only checking the address portion of an IP, and are independent from its prefix, as classful networks have no concept of prefix.

For more information on classful networks visit the Wikipedia page

Network design with IPAddress

IPAddress includes a lot of useful methods to manipulate IPv4 and IPv6 networks and do some basic network design.

Subnetting

The process of subnetting is the division of a network into smaller (in terms of hosts capacity) networks, called subnets, so that they all share a common root, which is the starting network.

For example, if you have network "172.16.10.0/24", we can subnet it into 4 smaller subnets. The new prefix will be /26, because 4 is 2^2 and therefore we add 2 bits to the network prefix (24+2=26).

Subnetting is easy with IPAddress. You actually have two options:

  • IPv4#subnet: specify a new prefix
  • IPv4#split: tell IPAddress how many subnets you want to create.

Let's examine IPv4#subnet first. Say you have network "172.16.10.0/24" and you want to subnet it into /26 networks.

network = IPAddress "172.16.10.0/24"

subnets = network.subnet(26)

subnets.map { |i| i.to_s } #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", "172.16.10.192/26"]

As you can see, an Array has been created, containing 4 new IPv4 objects representing the new subnets.

Another way to create subnets is to tell IPAddress how many subnets you'd like to have, and letting the library calculate the new prefix for you.

Let's see how it works, using IPv4#split method. Say you want 4 new subnets:

network = IPAddress("172.16.10.0/24")

subnets = network.split(4)

subnets.map { |i| i.to_s } #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", "172.16.10.192/26"]

Hey, that's the same result as before! This actually makes sense, as the two operations are complementary. When you use IPv4#subnet with the new prefix, IPAddress will always create a number of subnets that is a power of two. This is equivalent to use IPv4#split with a power of 2.

Where IPv4#split really shines is with the so called "uneven subnetting". You are not limited to split a network into a power-of-two numbers of subnets: IPAddress lets you create any number of subnets, and it will try to organize the new created network in the best possible way, making an efficient allocation of the space.

An example here is worth a thousand words. Let's use the same network as the previous examples:

network = IPAddress("172.16.10.0/24")

How do we split this network into 3 subnets? Easy:

subnets = network.split(3)

subnets.map { |i| i.to_s } #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/25"]

As you can see, IPAddress tried to perform a good allocation by filling up all the address space from the original network. There is no point in splitting a network into 3 subnets like 172.16.10.0/26, 172.16.10.64/26 and 172.16.10.128/26, as you would end up having 172.16.10.192/26 wasted.

We can go even further and split into 11 subnets:

network.split(11)
#=> ["172.16.10.0/28", "172.16.10.16/28", "172.16.10.32/28",
#    "172.16.10.48/28", "172.16.10.64/28", "172.16.10.80/28",
#    "172.16.10.96/28", "172.16.10.112/28", "172.16.10.128/27",
#    "172.16.10.160/27", "172.16.10.192/26"]

As you can see, most of the networks are /28, with a few /27 and one /26 to fill up the remaining space.

Summarization

Summarization (or aggregation) is the process when two or more networks are taken together to check if a supernet, including all and only these networks, exists. If it exists then this supernet is called the summarized (or aggregated) network.

It is important to understand that summarization can only occur if there are no holes in the aggregated network, or, in other words, if the given networks fill completely the address space of the supernet.

So the two rules are:

  1. The aggregate network must contain all the IP addresses of the original networks;

  2. The aggregate network must contain only the IP addresses of the original networks;

A few examples will help clarify the above. Let's consider for instance the following two networks:

ip1 = IPAddress("172.16.10.0/24")
ip2 = IPAddress("172.16.11.0/24")

These two networks can be expressed using only one IP address network if we change the prefix:

IPAddress::IPv4::summarize(ip1,ip2).map(&:to_s) #=> "172.16.10.0/23"

We note how the network 172.16.10.0/23 includes all the addresses specified in the above networks, and (more important) includes only those addresses.

If we summarized ip1 and ip2 with the following network:

"172.16.0.0/16"

we would have satisfied rule #1 above, but not rule #2. So

"172.16.0.0/16"

is not an aggregate network for ip1 and ip2.

If it's not possible to compute a single aggregated network for all the original networks, the method returns an array with all the aggregate networks found. For example, the following four networks can be aggregated in a single /22:

ip1 = IPAddress("10.0.0.1/24")
ip2 = IPAddress("10.0.1.1/24")
ip3 = IPAddress("10.0.2.1/24")
ip4 = IPAddress("10.0.3.1/24")

IPAddress::IPv4::summarize(ip1, ip2, ip3, ip4).map { |i| i.to_s } #=> ["10.0.0.0/22"]

But the following networks can't be summarized in a single network:

ip1 = IPAddress("10.0.1.1/24")
ip2 = IPAddress("10.0.2.1/24")
ip3 = IPAddress("10.0.3.1/24")
ip4 = IPAddress("10.0.4.1/24")

IPAddress::IPv4::summarize(ip1, ip2, ip3, ip4).map { |i| i.to_s } #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]

In this case, the two summarizables networks have been aggregated into a single /23, while the other two networks have been left untouched.

Supernetting

Supernetting is a different operation than aggregation, as it only works on a single network and returns a new single IPv4 object, representing the supernet.

Supernetting is similar to subnetting, except that you getting as a result a network with a smaller prefix (bigger host space). For example, given the network

ip = IPAddress("172.16.10.0/24")

you can supernet it with a new /23 prefix

ip.supernet(23).to_s #=> "172.16.10.0/23"

However if you supernet it with a /22 prefix, the network address will change:

ip.supernet(22).to_s #=> "172.16.8.0/22"

This is because 172.16.10.0/22 is not a network anymore, but an host address.

IPv6

IPAddress is not only fantastic for IPv4 addresses, it's also great to handle IPv6 addresses family! Let's discover together how to use it in our projects.

IPv6 addresses

IPv6 addresses are 128 bits long, in contrast with IPv4 addresses which are only 32 bits long. An IPv6 address is generally written as eight groups of four hexadecimal digits, each group representing 16 bits or two octet. For example, the following is a valid IPv6 address:

2001:0db8:0000:0000:0008:0800:200c:417a

Letters in an IPv6 address are usually written downcase, as per RFC. You can create a new IPv6 object using uppercase letters, but they will be converted.

Compression

Since IPv6 addresses are long to write, there are some simplifications and compressions that you can use to shorten them.

  • Leading zeroes: all the leading zeroes within a group can be omitted: "0008" would become "8"

  • A string of consecutive zeroes can be replaced by the string "::". This can be only applied once.

Using compression, the IPv6 address written above can be shorten into the following, equivalent, address

2001:db8::8:800:200c:417a

This short version is often used in human representation.

Network Mask

As we used to do with IPv4 addresses, an IPv6 address can be written using the prefix notation to specify the subnet mask:

2001:db8::8:800:200c:417a/64

The /64 part means that the first 64 bits of the address are representing the network portion, and the last 64 bits are the host portion.

Using IPAddress with IPv6 addresses

All the IPv6 representations we've just seen are perfectly fine when you want to create a new IPv6 address:

ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200C:417A"

ip6 = IPAddress "2001:db8:0:0:8:800:200C:417A"

ip6 = IPAddress "2001:db8:8:800:200C:417A"

All three are giving out the same IPv6 object. The default subnet mask for an IPv6 is 128, as IPv6 addresses don't have classes like IPv4 addresses. If you want a different mask, you can go ahead and explicit it:

ip6 = IPAddress "2001:db8::8:800:200c:417a/64"

Access the address portion and the prefix by using the respective methods:

ip6 = IPAddress "2001:db8::8:800:200c:417a/64"

ip6.address #=> "2001:0db8:0000:0000:0008:0800:200c:417a"

ip6.prefix #=> 64

A compressed version of the IPv6 address can be obtained with the IPv6#compressed method:

ip6 = IPAddress "2001:0db8:0000:0000:0008:200c:417a:00ab/64"

ip6.compressed #=> "2001:db8::8:800:200c:417a"

Handling the IPv6 address

Accessing the groups that form an IPv6 address is easy with the IPv6#groups method:

ip6 = IPAddress "2001:db8::8:800:200c:417a/64"

ip6.groups #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]

As with IPv4 addresses, each individual group can be accessed using the IPv6#[] shortcut method:

ip6[0] #=> 8193
ip6[1] #=> 3512
ip6[2] #=> 0
ip6[3] #=> 0

Note that each 16 bits group is expressed in its decimal form. You can also obtain the groups into hexadecimal format using the IPv6#hexs method:

ip6.hexs #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]

A few other methods are available to transform an IPv6 address into decimal representation, with IPv6.to_i

ip6.to_i #=> 42540766411282592856906245548098208122

or to hexadecimal representation:

ip6.to_hex #=> "20010db80000000000080800200c417a"

To print out an IPv6 address in human readable form, use the IPv6#to_string, IPv6#to_s and IPv6#to_string_uncompressed methods:

ip6 = IPAddress "2001:db8::8:800:200c:417a/64"

ip6.to_string #=> "2001:db8::8:800:200c:417a/96"
ip6.to_s #=> "2001:db8::8:800:200c:417a/96"

ip6.to_string_uncompressed #=> "2001:0db8:0000:0000:0008:0800:200c:417a/96"

As you can see, IPv6.to_string prints out the compressed form, while IPv6.to_string_uncompressed uses the expanded version.

Compressing and uncompressing

If you have a string representing an IPv6 address, you can easily compress it and uncompress it using the two class methods IPv6::expand and IPv6::compress.

For example, let's say you have the following uncompressed IPv6 address:

str = "2001:0DB8:0000:CD30:0000:0000:0000:0000"

Here is the compressed version:

IPAddress::IPv6.compress(str) #=> "2001:db8:0:cd30::"

The other way works as well:

str = "2001:db8:0:cd30::"

IPAddress::IPv6.expand(str) #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"

These methods can be used when you don't want to create a new object just for expanding or compressing an address (although a new object is actually created internally).

New IPv6 address from other formats

You can create a new IPv6 address from different formats than just a string representing the colon-hex groups.

For instance, if you have a data stream, you can use IPv6::parse_data, like in the following example:

data = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"

ip6 = IPAddress::IPv6::parse_data data
ip6.prefix = 64

ip6.to_s #=> "2001:db8::8:800:200c:417a/64"

A new IPv6 address can also be created from an unsigned 128 bits integer:

u128 = 42540766411282592856906245548098208122

ip6 = IPAddress::IPv6::parse_u128 u128
ip6.prefix = 64

ip6.to_s #=>"2001:db8::8:800:200c:417a/64"

Finally, a new IPv6 address can be created from an hex string:

hex = "20010db80000000000080800200c417a"

ip6 = IPAddress::IPv6::parse_hex hex
ip6.prefix = 64

ip6.to_s #=> "2001:db8::8:800:200c:417a/64"

Special IPv6 addresses

Some IPv6 have a special meaning and are expressed in a special form, quite different than an usual IPv6 address. IPAddress has built-in support for unspecified, loopback and mapped IPv6 addresses.

Unspecified address

The address with all zero bits is called the unspecified address (corresponding to 0.0.0.0 in IPv4). It should be something like this:

0000:0000:0000:0000:0000:0000:0000:0000

but, with the use of compression, it is usually written as just two colons:

::

or, specifying the netmask:

::/128

With IPAddress, create a new unspecified IPv6 address using its own subclass:

ip = IPAddress::IPv6::Unspecified.new

ip.to_s #=> "::/128"

You can easily check if an IPv6 object is an unspecified address by using the IPv6#unspecified? method

ip.unspecified? #=> true

An unspecified IPv6 address can also be created with the wrapper method, like we've seen before

ip = IPAddress "::"

ip.unspecified? #=> true

This address must never be assigned to an interface and is to be used only in software before the application has learned its host's source address appropriate for a pending connection. Routers must not forward packets with the unspecified address.

Loopback address

The loopback address is a unicast localhost address. If an application in a host sends packets to this address, the IPv6 stack will loop these packets back on the same virtual interface.

Loopback addresses are expressed in the following form:

::1

or, with their appropriate prefix,

::1/128

As for the unspecified addresses, IPv6 loopbacks can be created with IPAddress calling their own class:

ip = IPAddress::IPv6::Loopback.new

ip.to_s #=> "::1/128"

or by using the wrapper:

ip = IPAddress "::1"

ip.to_s #=> "::1/128"

Checking if an address is loopback is easy with the IPv6#loopback? method:

ip.loopback? #=> true

The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.

Mapped address

It is usually identified as a IPv4 mapped IPv6 address, a particular IPv6 address which aids the transition from IPv4 to IPv6. The structure of the address is

::ffff:w.y.x.z

where w.x.y.z is a normal IPv4 address. For example, the following is a mapped IPv6 address:

::ffff:192.168.100.1

IPAddress is powerful in handling mapped IPv6 addresses, as the IPv4 portion is stored internally as a normal IPv4 object. Let's have a look at some examples. To create a new mapped address, just use the class builder itself

ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"

or just use the wrapper method

ip6 = IPAddress "::ffff:172.16.10.1/128"

Let's check it's really a mapped address:

ip6.mapped? #=> true

ip6.to_s #=> "::ffff:172.16.10.1/128"

Now with the #ipv4 attribute, we can easily access the IPv4 portion of the mapped IPv6 address:

ip6.ipv4.address #=> "172.16.10.1"

Internally, the IPv4 address is stored as two 16 bits groups. Therefore all the usual methods for an IPv6 address are working perfectly fine:

ip6.to_hex #=> "00000000000000000000ffffac100a01"

ip6.address #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"

A mapped IPv6 can also be created just by specify the address in the following format:

ip6 = IPAddress "::172.16.10.1"

That is, two colons and the IPv4 address. However, as by RFC, the ffff group will be automatically added at the beginning

ip6.to_s #=> "::ffff:172.16.10.1/128"

making it a mapped IPv6 compatible address.

Why not using IPAddr?

IPAddr is the IP addresses library that comes with Ruby standard lib. We found this library, although well written, not suitable for all our needs.

Some quick examples of things you can't do with IPAddr:

  • store both the address and the prefix information
  • quickly find the broadcast address of a network
  • iterate over hosts
  • perform subnetting or network aggregation

Many methods and procedures are so old that they have been declared deprecated by the IETF, and some others have bugs in their implementation.

Moreover, IPAddress is more robust and is roughly twice as fast as IPAddr, in addition to provide an organic API with logical separation and code structure.

We hope that IPAddress will address all these issues and meet all your needs in network programming.

Community

Want to join the community?

We have discussions setup on Github, where anyone can participate and discuss.

Thanks to

Thanks to Luca Russo (vargolo) and Simone Carletti (weppos) for all the support and technical review. Thanks to Marco Beri, Bryan T. Richardson, Nicolas Fevrier, jdpace, Daniele Alessandri, jrdioko, Ghislain Charrier, Pawel Krzesniak, Mark Sullivan, Leif Gensert, Erik Ahlstrรถm, Peter Vandenberk and Steve Rawlinson for their support, feedback and bug reports.

Copyright

Copyright (c) 2009-today Marco Ceresa and Mike Mackintosh. See LICENSE for details.

ipaddress's People

Contributors

asomers avatar bluemonk avatar brohee avatar brona avatar codegoalie avatar eahlstrom avatar francisluong avatar gdlx avatar ghg avatar havenwood avatar icy-arctic-fox avatar jwillemsen avatar kachick avatar kaoudis avatar koic avatar krpk1900 avatar mikemackintosh avatar mikerodrigues avatar nicolasleger avatar nrk avatar paudam avatar pedro-nonfree avatar sandstrom avatar smortex avatar stevschmid avatar vanderhoorn avatar weppos avatar wpiekutowski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ipaddress's Issues

When the new gem version will be issued?

Some changes have been made to a gem since v0.8.3. One of them is fixing the issue with IPv4#<=> (#76) which is important for what I'm currently doing (I'm forced to put a hack in my code to avoid it). So when do you plan to issue a new gem version?

Avoid Ruby 1.8 compatibility when using Ruby 2.1.8

There's a small problem in https://github.com/bluemonk/ipaddress/blob/master/lib/ipaddress.rb#L217.
This regular expression is matching not only Ruby 1.8, but also 2.1.8. Because of that, when you upgrade to any Ruby with a version similar to x.1.8, you'll get a ton of warnings similar to:

.rbenv/versions/2.1.8/lib/ruby/gems/2.1.0/gems/sprockets-2.11.3/lib/sprockets/mime.rb:25: warning: Hash#index is deprecated; use Hash#key

That code is correctly using Hash#key, but due to the alias made in

https://github.com/bluemonk/ipaddress/blob/master/lib/ipaddress.rb#L219

Hash#key becomes an alias to Hash#index and actually Hash#index is called, which results in a lot of depreciation warnings.

Invalid IPv6 handling

Hi guys,

i have found a bug.

It is allowed to use dot notation in ipv6. The following address is not a mapped ipv6 address.

IPAddress.parse('abcd:9234::1.2.3.4/96').to_string

I got a wrong output
=> ::ffff:9.2.3.4/96

But the correct output must be:
=> abcd:9234::102:304/96

Same problem here:

IPAddress.parse('::1.2.3.4/120').to_string
=> "::ffff:1.2.3.4/120"

Correct output is
=> ::102:304/120

Only
IPAddress.parse('::ffff:1.2.3.4/120') is a mapped ipv6 value!
Only here it is allowed to handle it as an Mapped IPv6 Object with the following output:
=> "::ffff:1.2.3.4/120"

Add Travis CI for this repo

As maintainer or contributor for this repo, I would like branches to be automatically tested using Travis CI.

Extra files in the gem

Hello:

I'm packaging this up as an RPM for Fedora (and in the future, EPEL) as a dependency for Chef, and during the packaging process I noticed that the gem has several unneeded files in it, namely .document and (arguably) the gemspec. I'm manually excluding them in the RPM but I thought I'd bring it to your attention for your next release.

Comparing an IPv4-address with IPv6-address failed

Hi there,
I'm tried the gem and the latest version of IPAddress from the git repository. In both cases I got an error when comparing an IPv4-address with an IPv6-address. Can you help me with suggestions to work around the problem?

[1] pry(#<ApplProxyMgt::ForwardingEntry>)> IPAddress.parse('127.0.0.1').include? IPAddress.parse('::1')
NoMethodError: undefined method `to_u32' for #<IPAddress::IPv6:0x00000005e46a38>
from /home/user/.gem/ruby/2.2.0/bundler/gems/ipaddress-ce5b52474875/lib/ipaddress/ipv4.rb:537:in `include?'

using split() fails if you request more than 32 networks

a Class B network, like "10.13.0.0/16" contains 256 Class C networks [1]. consider the example code:

require 'ipaddress'
x = IPAddress('10.13.0.0/16')
c_class_count = (x.hosts.length / 256) + 1
x.split(c_class_count)

this results in the following error:

ArgumentError: New prefix must be between 16 and 32
from PATH/vendor/cache/ruby/1.9.1/gems/ipaddress-0.8.0/lib/ipaddress/ipv4.rb:684:in `subnet'

I also used "10.0.0.0/8" and reproduced the same behavior.

[1] http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#IPv4_CIDR_blocks

derive host address from network

given a network and a host portion, is it possible for IPAddress to generate the full host address in that network? for example, if i've got an IPAddress('1.1.1.0/24'), can i ask it for the full address of a host whose host bits are 123 (i.e. 1.1.1.123)?

i searched various methods and did not see anything direct (yes, you can splice octets, but that only works reliably for /8, /16, and /24 networks). there are useful applications in templating, e.g. "i'm planning IPs for a new network, and hosts of a given role always have the host bits 123, so please give me the full address of that host in this network"

Wording in IPv4#include? documentation

The following sentence is in the IPAddress::IPv4#include? documentation: "Accepts either string with the IP or and IPAddress::IPv4 object." In addition to the typo ("or and"), the sentence doesn't seem accurate (the source makes it look like it needs an object, not a simple string containing the IP).

Feature request to allow counting backwards on a network.

I would like a new feature.

Currently NetAddr::CIDR.create('10.0.0.1/31')[0] would give us 10.0.0.0/32 and likewise [1] gives us 10.0.0.1/32.
But I feel that NetAddr::CIDR.create('10.0.0.1/31')[-1] should also give us 10.0.0.1/32. I'm already writing code above NetAddr in a project I'm working on to do this but it would be nice to see the Gem itself support it.

Having the ability to count backwards from the end like Ruby allows us with Arrays is handy for calculating gateways and other kinds of reserved addresses.

Can we get something like this added?

Wrong broadcast address

Hello community,

i got a problem with the IPAddress lib, i dont know if it's a bug, or just a mistake from me.

I have this address ' 46.17.68.76/31 ', i want to have the broadcast addess, then i make :

network = "46.17.68.76/31"
@ip     = IPAddress.parse(network)
puts @ip.broadcast 

the output : @address="255.255.255.255"

but for me the broadcast address for ' 46.17.68.76/31 ' is : 46.17.68.77

Thank's for your help!

Stack overflow doing IPAddress#as_json

The IPAddress gem abuses the Enumerable mixin. One example is the each method. That method will always yield at least one value, which causes problems for other Ruby code that assumes an Enumerable cannot contain itself. For example, the following snippet will trigger a stack overflow:

require 'ipaddress'
require 'active_support/json'
IPAddress.parse("1.2.3.4/32").as_json

The best solution would be to draw a distinction between an address and a network, like Python3's ipaddress module. If that were the case, then IPNetwork#each would yield a bunch of IPAddress objects, which would not implement #each.

A smaller change that wouldn't break compatibility with existing IPAddress users to be to define an as_json method which would simply call to_string.

Add address_scope methods

Add an address_scope method (or possibly 'scope') which will return a text string describing the type of ip address (Looback, autoconfigured, Multicast, RFC1918 etc)

Anyone have objections

IPAddress.parse("1") => doesn't fail.

Is this really correct behaviour? It certainly wasn't the parse exception I expected.

IPAddress::parse "1"
=> #<IPAddress::IPv6:0xb73ba5bc @compressed="1::", @Prefix=128, @address="0001:0000:0000:0000:0000:0000:0000:0000", @groups=[1, 0, 0, 0, 0, 0, 0, 0]>

As a comparison,

IPAddress::parse "111111"
ArgumentError: Invalid IP "111111"

Gem was built in 2011

The latest version of this gem was released on May 16, 2011, but I can see that there have been several commits since then.

Should we be using the gem or would it be better to use the Github repo in our Gemfiles? Is there any reason why the latest commits have not been bundled into the official gem?

issue in version 0.6.0

as I said before, here is the bug

using ipaddress version 0.6.0

001:0> require 'ipaddress'
==> true

002:0> ip = IPAddress '192.168.1.101/255.255.255.248'
==> #<IPAddress::IPv4:0x82dff64 @address="192.168.1.101", @Prefix=29, @Octets=[192, 168, 1, 101]>

003:0> ip.first.to_s
NoMethodError: undefined method to_u32' for 29:IPAddress::Prefix from /usr/lib/ruby/gems/1.9.1/gems/ipaddress-0.6.0/lib/ipaddress/ipv4.rb:510:innetwork_u32'
from /usr/lib/ruby/gems/1.9.1/gems/ipaddress-0.6.0/lib/ipaddress/ipv4.rb:356:in `first'
from (irb):3

from /usr/bin/irb:12:in `'

while if I use /29 as subnet mask prefix, it works fine

=> ip = IPAddress '192.168.1.101/29'

=> ip.first.to_s

 #=> "192.168.1.97"

Random IPv6

Thanks for this gem. I have an IPv6 /64 where I want to get a random IP from, what's the most efficient way to do this?

Add Contributing.md to this repo

As a contributor to this repo, I would like guidelines to be identified in the root of this repo in a file named contributing.md

IPAddress.parse("<mac_address>") results in IPv6 address object

Hi,

I'm not sure if this is the expected behavior:

> IPAddress.parse("01:1c:43:10:20:0a")
 => #<IPAddress::IPv6:0x007fd780b9a1b8 @groups=[1, 28, 67, 16, 32, 10, 0, 0], @address="0001:001c:0043:0010:0020:000a:0000:0000", @compressed="1:1c:43:10:20:a::", @prefix=128>

In my understanding, the representation of an IPv6 address must either be fully qualified (7 colons) or there must be two consecutive colons (::) for zero padding. The mac address has only 5 colons and parse() pads it with trailing zeros.

So shouldn't it better fail?

I'm using ipaddress 0.8.0 with ruby 2.1.5.

Thanks,
Oliver

ipv4 abbreviation not supported

IPAddress::valid_ipv4? '10.1'
=> false

$ ping 10.1
PING 10.1 (10.0.0.1): 56 data bytes

to be honest, not that critical, but still for completenes...

Regexp for IPv6 addresses

I noticed you have a REGEXP constant for ipv4 addresses. Do you have an equivalent for ipv6 addresses? Seems like that would be good to have too.

ipaddress gem breaks aws-sdk RESTXMLClient

ipaddress is used with the Chef Ohai component, which caused a really bizarre problem: When aws-sdk (1.51.0, the newest atm) is used to do a create_health_check request it can't correctly send xml property named "IPAddress" to the aws api.

How to reproduce with irb:

require 'ipaddress'
require 'aws-sdk'
r53 = AWS::Route53.new(:access_key_id => "....",:secret_access_key => "...",:http_wire_trace => true)
options = {:caller_reference => "foo",:health_check_config => {:ip_address => "54.84.217.101",:type => "HTTP",:resource_path => "/check"}}
r53.client.create_health_check(options)

This prints that the following data is sent to the api:
"<CreateHealthCheckRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\">\n <CallerReference>foo</CallerReference>\n <HealthCheckConfig>\n <Type>HTTP</Type>\n <ResourcePath>/check</ResourcePath>\n </HealthCheckConfig>\n</CreateHealthCheckRequest>"
(notice that there's no <IPAddress>54.84.217.101</IPAddress> tag)

Now another irb session without ipaddress gem:

require 'aws-sdk'
r53 = AWS::Route53.new(:access_key_id => "....",:secret_access_key => "...",:http_wire_trace => true)
options = {:caller_reference => "foo",:health_check_config => {:ip_address => "54.84.217.101",:type => "HTTP",:resource_path => "/check"}}
r53.client.create_health_check(options)

prints:
"<CreateHealthCheckRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\">\n <CallerReference>foo</CallerReference>\n <HealthCheckConfig>\n <IPAddress>54.84.217.101</IPAddress>\n <Type>HTTP</Type>\n <ResourcePath>/check</ResourcePath>\n </HealthCheckConfig>\n</CreateHealthCheckRequest>"

So the ipaddress module is somehow monkey-patching something which breaks the aws-cli. the aws-cli builds the requests based on a yaml config which maps ruby property names into xml tags. Editing this yml and replacing the "IPAddress" tag definition with another name, say "IPAddress2" will result that the tag is rendered correctly.

There's at least one other user who was struct with this very same bug: https://forums.aws.amazon.com/thread.jspa?threadID=128785

Replicating IPAddr functionality (from_u32)

We store IP addresses in our database as integers. I am looking at replacing IPAddr with IPAddress, but I can not find a method to convert fixnum to dot notation. Does one exist? If not, is one on the roadmap?

The IPAddr way to achieve dot notation from integer:
IPAddr.new([fixnum], Socket::AF_INET).to_s

.first and .last method of an IPv4 /32 address is not working properly

In version 0.8.0

I get following result using .first and .last on an IPv4 /32 address:
[66] pry(main)> ip = IPAddress('192.168.1.2/32')
=> #<IPAddress::IPv4:0xc001e58
@address="192.168.1.2",
@Octets=[192, 168, 1, 2],
@Prefix=32,
@u32=3232235778>
[67] pry(main)> ip.first
=> #<IPAddress::IPv4:0xc02fed4
@address="192.168.1.3",
@Octets=[192, 168, 1, 3],
@Prefix=32,
@u32=3232235779>
[68] pry(main)> ip.last
=> #<IPAddress::IPv4:0xc052498
@address="192.168.1.1",
@Octets=[192, 168, 1, 1],
@Prefix=32,
@u32=3232235777>

IPv? and Prefix? classes don't work as expected in hashes and sets

In the following examples I would expect the set to only have one entry and the hash to only have one key (note: if it's the same object instance you won't see this behaviour).

require 'set'
require 'ipaddress'

# works as expected
net =  IPAddress.parse('192.168.0.0/24')
s = Set.new
s.add net
s.add net
puts s.length # => 1

# doesn't work as expected
s = Set.new
s.add IPAddress.parse('192.168.0.0/24')
s.add IPAddress.parse('192.168.0.0/24')
puts s.length # => 2, should be 1?

# likewise with hashes
# works as expected
net =  IPAddress.parse('192.168.0.0/24')
h = {}
h[net] = true
h[net] = false
puts h.length # => 1

# doesn't work as expected
h = {}
h[IPAddress.parse('192.168.0.0/24')] = true
h[IPAddress.parse('192.168.0.0/24')] = false
puts h.length # => 2, should be 1?

`link_local?` defined twice in `lib/ipaddress/ipv6.rb`

Noticed this error during tests in an unrelated project:

/usr/local/rvm/gems/ruby-2.3.0/gems/ipaddress-0.8.3/lib/ipaddress/ipv6.rb:437: warning: method redefined; discarding old link_local?
/usr/local/rvm/gems/ruby-2.3.0/gems/ipaddress-0.8.3/lib/ipaddress/ipv6.rb:405: warning: previous definition of link_local? was here

Seems link_local? is defined twice in lib/ipaddress/ipv6.rb

https://github.com/ipaddress-gem/ipaddress/blob/master/lib/ipaddress/ipv6.rb#L405

    #
    # Returns true if the address is a link local address
    #
    def link_local?
      @groups[0] == 0xfe80
    end

https://github.com/ipaddress-gem/ipaddress/blob/master/lib/ipaddress/ipv6.rb#L437

    #
    # Checks if an IPv6 address objects belongs
    # to a link-local network RFC4291
    #
    # Example:
    #
    #   ip = IPAddress "fe80::1"
    #   ip.link_local?
    #     #=> true
    #
    def link_local?
      [self.class.new("fe80::/64")].any? {|i| i.include? self}
    end

Implement NetAddr::CIDR#allocate_rfc3531

Hi,

We're currently using the NetAddr gem to provide our software with supernet, subnet and IP Address management. An example of this as follows:

ruby-1.9.2-p180 :004 > Supernet.create(:network => '192.168.0.0', :cidr => '20')
 => 192.168.0.0/20 
ruby-1.9.2-p180 :005 > Vlan.first.allocate_subnet(29)
 => 192.168.0.0/29 
ruby-1.9.2-p180 :006 > Vlan.first.allocate_subnet(28)
 => 192.168.0.16/28 
ruby-1.9.2-p180 :007 > Vlan.first.allocate_subnet(27)
 => 192.168.0.32/27 
ruby-1.9.2-p180 :008 > Vlan.first.allocate_subnet(29)
 => 192.168.0.8/29 
ruby-1.9.2-p180 :009 > 

We use the allocate_rfc351 function in NetADDR::CIDR to do this currently, but would like to use your library as it has a lot nicer API. Would you be able to duplicate the functionality in your better API?

Thanks

each_host function too slow

Hi,

I encountered an issue about the execution time of each_host in ipv4 (I have not checked for ipv6)

If you replace the code of each_host by tu same one as the one from each, the execution time is twice faster. Indeed, each_hosts uses "hosts" which itself calls each. Therefore, the array is iterated over twice.

By replacing the code likewise, the array is just iterated over once:

def each_host
    (network_u32 + 1..broadcast_u32 - 1).each do |i|
        yield self.class.parse_u32(i, @prefix)
    end
end

Here is the example code. Just replace each_host by host and see the execution time difference:

require 'rubygems'
require 'ipaddress'
ip = IPAddress "10.8.0.0/14"
ip.each_host do |ip|
    ip
end

Publish 0.8.2

Seems 0.8.0 is the latest I can depend on, can you publish the newer ones? Thank you for this gem!

IPv4#self_assigned? and IPv4#global?

I think checking 169.254.*.* for self_assigned? would be useful.

Also ideally, global? to find out if the IP is reachable from Internet would be awesome, if possible.

What do you think?

Ranges support

Please provide way to use ip addresses with ranges.

ip1 = IPAddress('192.168.10.10')
ip2 = IPAddress('192.168.30.40')

ip = IPAddress('192.168.15.20')

(ip1..ip2).include? ip

Changing octets doesn't change address

When I change a value in .octets I expect the .address to be changes. Than doesn't occur.
Version 0.8.0 is used.

realloc@support01:/var/tmp$ irb
1.9.3-p194 :001 > require 'ipaddress'
 => true 
1.9.3-p194 :002 > ip = IPAddress("172.16.10.1/24")
 => 172.16.10.1 
1.9.3-p194 :003 > ip.address
 => "172.16.10.1" 
1.9.3-p194 :004 > ip.octets[2]=11
 => 11 
1.9.3-p194 :005 > ip.address
 => "172.16.10.1" 
1.9.3-p194 :006 > ip[2]
 => 11 
1.9.3-p194 :007 > 

undefined method `link_local?' for #<IPAddress::IPv4:0x007fbd8353e880>

To reproduce

> ip_adr = IPAddress('192.168.0.1')
> ip_adr.link_local?

or

> ip_adr = IPAddress::IPv4.new('192.168.0.1')
> ip_adr.link_local?

I get this error:

NoMethodError: undefined method `link_local?' for #<IPAddress::IPv4:0x007f9c7c790a70>
  from (irb):19
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/railties-4.2.8/lib/rails/commands/console.rb:110:in `start'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/railties-4.2.8/lib/rails/commands/console.rb:9:in `start'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/railties-4.2.8/lib/rails/commands/commands_tasks.rb:68:in `console'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/railties-4.2.8/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/railties-4.2.8/lib/rails/commands.rb:17:in `<top (required)>'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:274:in `require'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:274:in `block in require'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:240:in `load_dependency'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:274:in `require'
  from /Users/redapc/Sites/INSPINIA-WB0R5L90S/easyforce/bin/rails:9:in `<top (required)>'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:268:in `load'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:268:in `block in load'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:240:in `load_dependency'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.8/lib/active_support/dependencies.rb:268:in `load'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/commands/rails.rb:6:in `call'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/command_wrapper.rb:38:in `call'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application.rb:201:in `block in serve'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application.rb:171:in `fork'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application.rb:171:in `serve'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application.rb:141:in `block in run'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application.rb:135:in `loop'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application.rb:135:in `run'
  from /Users/redapc/.rvm/gems/ruby-2.3.1/gems/spring-2.0.2/lib/spring/application/boot.rb:19:in `<top (required)>'
  from /Users/redapc/.rvm/rubies/ruby-2.3.1/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
  from /Users/redapc/.rvm/rubies/ruby-2.3.1/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
  from -e:1:in `<main>'

NoMethodError: undefined method `loopback?' for 127.0.0.1:IPAddress::IPv4

I'm trying to check if my IP is a loopback ip, I get this:

ip = IPAddress('127.0.0.1').loopback?
NoMethodError: undefined method `loopback?' for 127.0.0.1:IPAddress::IPv4
    from (irb):9
    from /usr/local/Cellar/ruby/1.9.3-p194/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/commands/console.rb:47:in `start'
    from /usr/local/Cellar/ruby/1.9.3-p194/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/commands/console.rb:8:in `start'
    from /usr/local/Cellar/ruby/1.9.3-p194/lib/ruby/gems/1.9.1/gems/railties-3.2.8/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

Any ideas?

Is this gem still under active development?

The last commit was almost 9 months ago, and there are 26 open issues, many of which have no response, and some of which provide pull-requests that have not been incorporated.

Is there an alternative gem for handling IP addresses or does IPAddr make this one redundant?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.