class IPAddress::IPv4

Overview

Class IPAddress::IPv4 is used to handle IPv4 type addresses.

Included Modules

Defined in:

ipaddress/ipv4.cr

Constant Summary

CLASSFUL = {/^0../ => 8, /^10./ => 16, /^110/ => 24}

This Hash contains the prefix values for classful networks:

  • 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, D and E, from 192.0.0.0 to 255.255.255.254

NOTE Classes C, D and E will all have a default prefix of /24 or 255.255.255.0.

REGEXP = /(0|[1-9]{1}\d{0,2})\.(0|[1-9]{1}\d{0,2})\.(0|[1-9]{1}\d{0,2})\.(0|[1-9]{1}\d{0,2})/

Regular expression to match an IPv4 address

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module IPAddress

ipv4? ipv4?, ipv6? ipv6?

Constructor methods inherited from module IPAddress

new(addr : String | Int) : IPAddress new, parse(addr : String | Int) : IPAddress parse

Class methods inherited from module IPAddress

aton(addr : String) : UInt32 aton, ntoa(uint : UInt32) : String
ntoa(int : Int) : String
ntoa
, valid?(addr : String) valid?, valid_ipv4?(addr : String) valid_ipv4?, valid_ipv4_netmask?(addr : String) valid_ipv4_netmask?, valid_ipv6?(addr : String) valid_ipv6?

Constructor Detail

def self.extract(string : String) : IPv4 #

Extract an IPv4 address from a string and returns a new object.

ip = IPAddress::IPv4.extract "foobar172.16.10.1barbaz"
ip.to_s # => "172.16.10.1"

[View source]
def self.new(addr : String, netmask = nil) #

Creates a new IPv4 address object.

An IPv4 address can be expressed in any of the following forms:

  • 10.1.1.1/24: ip address and prefix. This is the common and suggested way to create an object
  • 10.1.1.1/255.255.255.0: ip address and netmask. Although convenient sometimes, this format is less clear than the previous one
  • 10.1.1.1: if the address alone is specified, the prefix will be set as default 32, also known as the host prefix
# These two are the same:
ip = IPAddress::IPv4.new "10.0.0.1/24"
ip = IPAddress.new "10.0.0.1/24"

# These two are the same
ip = IPAddress::IPv4.new "10.0.0.1/8"
ip = IPAddress::IPv4.new "10.0.0.1/255.0.0.0"

[View source]
def self.parse_classful(ip : String) : IPv4 #

Creates a new IPv4 address object by parsing the address in a classful way.

Classful addresses have a fixed netmask based on the class they belong to:

  • 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, D and E, from 192.0.0.0 to 255.255.255.254
ip = IPAddress::IPv4.parse_classful "10.0.0.1"

ip.netmask # => "255.0.0.0"
ip.a?      # => true

NOTE Classes C, D and E will all have a default prefix of /24 or 255.255.255.0.


[View source]
def self.parse_data(data : Bytes, prefix = 32) : IPv4 #

Creates a new IPv4 object from binary data, like the one you get from a network stream.

For example, on a network stream the IP 172.16.0.1 is represented with the binary Bytes[172, 16, 0, 1].

ip = IPAddress::IPv4.parse_data Bytes[172, 16, 0, 1]
ip.prefix = 24

ip.to_string # => "172.16.10.1/24"

[View source]
def self.parse_u32(u32, prefix = 32) : IPv4 #

Creates a new IPv4 object from an unsigned 32 bits integer.

ip = IPAddress::IPv4.parse_u32 167772160

ip.prefix = 8
ip.to_string # => "10.0.0.0/8"

The prefix parameter is optional:

ip = IPAddress::IPv4.parse_u32 167772160, 8
ip.to_string # => "10.0.0.0/8"

[View source]

Class Method Detail

def self.summarize(ips : Array(IPv4)) : Array(IPv4) #

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 very 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.new "172.16.10.0/24"
ip2 = IPAddress.new "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_string
# => ["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.new "10.0.0.1/24"
ip2 = IPAddress.new "10.0.1.1/24"
ip3 = IPAddress.new "10.0.2.1/24"
ip4 = IPAddress.new "10.0.3.1/24"

IPAddress::IPv4.summarize(ip1, ip2, ip3, ip4).map &.to_string
# => ["10.0.0.0/22"]

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

ip1 = IPAddress.new "10.0.1.1/24"
ip2 = IPAddress.new "10.0.2.1/24"
ip3 = IPAddress.new "10.0.3.1/24"
ip4 = IPAddress.new "10.0.4.1/24"

IPAddress::IPv4.summarize(ip1, ip2, ip3, ip4).map &.to_string
# => ["10.0.1.0/24", "10.0.2.0/23", "10.0.4.0/24"]

[View source]
def self.summarize(*ips : IPv4) : Array(IPv4) #

ditto


[View source]
def self.valid?(addr : String) #

Returns true if the given string is a valid IPv4 address.

IPAddress::IPv4.valid? "172.16.10.1" # => true
IPAddress::IPv4.valid? "2002::1"     # => false

[View source]
def self.valid_netmask?(addr : String) #

Returns true if the argument is a valid IPv4 netmask expressed in dotted decimal format.

IPAddress::IPv4.valid_netmask? "255.255.0.0" # => true

[View source]

Instance Method Detail

def +(other : IPv4) : Array(IPv4) #

Returns a new IPv4 object which is the result of the summarization, if possible, of the two objects.

ip1 = IPAddress.new "172.16.10.1/24"
ip2 = IPAddress.new "172.16.11.2/24"

(ip1 + ip2).map &.to_string # => ["172.16.10.0/23"]

If the networks are not contiguous, returns the two network numbers from the objects:

ip1 = IPAddress.new "10.0.0.1/24"
ip2 = IPAddress.new "10.0.2.1/24"

(ip1 + ip2).map &.to_string # => ["10.0.0.0/24", "10.0.2.0/24"]

[View source]
def -(other : IPv4) : UInt32 #

Returns the difference between two IP addresses in unsigned int 32 bits format.

ip1 = IPAddress.new "172.16.10.0/24"
ip2 = IPAddress.new "172.16.11.0/24"

ip1 - ip2 # => 256

[View source]
def /(subnets : Int32) : Array(IPv4) #

ditto


[View source]
def <=>(other : IPv4) #

Spaceship operator to compare IPv4 objects.

Comparing IPv4 addresses is useful to ordinate them into lists that match our intuitive perception of ordered IP addresses.

The first comparison criteria is the u32 value. For example, 10.100.100.1 will be considered to be less than 172.16.0.1, because, in an ordered list, we expect 10.100.100.1 to come before 172.16.0.1.

The second criteria, in case two IPv4 objects have identical addresses, is the prefix. An higher prefix will be considered greater than a lower prefix. This is because we expect to see 10.100.100.0/24 come before 10.100.100.0/25.

ip1 = IPAddress.new "10.100.100.1/8"
ip2 = IPAddress.new "172.16.0.1/16"
ip3 = IPAddress.new "10.100.100.1/16"

ip1 < ip2 # => true
ip1 > ip3 # => false

[ip1, ip2, ip3].sort.map &.to_string
# => ["10.100.100.1/8", "10.100.100.1/16", "172.16.0.1/16"]

[View source]
def ==(other : self) #
Description copied from class Reference

Returns true if this reference is the same as other. Invokes same?.


def [](index : Int32) : Int32 #

Returns the octet specified by index.

ip = IPAddress.new "172.16.100.50/24"

ip[0] # => 172
ip[1] # => 16
ip[2] # => 100
ip[3] # => 50

See also: #octets


[View source]
def []=(index : Int32, value : Int32) : Nil #

Updates the octet specified at index.

ip = IPAddress.new "172.16.100.50/24"
ip[2] = 200

# => #<IPAddress::IPv4:0x00000000000000 @address="172.16.200.1",
# => @prefix=32, @octets=[172, 16, 200, 1], @u32=2886780929>

See also: #octets


[View source]
def a? #

Checks whether the ip address belongs to a RFC791 CLASS A network, no matter what the subnet mask is.

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

[View source]
def address : String #

Returns the address portion of the IPv4 object as a string.

ip = IPAddress.new "172.16.100.4/22"
ip.address # => "172.16.100.4"

[View source]
def b? #

Checks whether the ip address belongs to a RFC791 CLASS B network, no matter what the subnet mask is.

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

[View source]
def bits : String #

Returns the address portion of an IP in binary format, as a string containing a sequence of 0 and 1.

ip = IPAddress.new "127.0.0.1"
ip.bits # => "01111111000000000000000000000001"

[View source]
def broadcast : IPv4 #

Returns the broadcast address for the given IP.

ip = IPAddress.new "172.16.10.64/24"
ip.broadcast.to_s # => "172.16.10.255"

[View source]
def broadcast_u32 : UInt32 #

Returns the broadcast address in unsigned 32 bits format.

ip = IPaddress.new "10.0.0.1/29"
ip.broadcast_u32 # => 167772167

[View source]
def c? #

Checks whether the ip address belongs to a RFC791 CLASS C network, no matter what the subnet mask is.

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

[View source]
def data : Bytes #

Returns the address portion of an IPv4 object in a network byte order format (IO::ByteFormat::NetworkEndian).

ip = IPAddress.new "172.16.10.1/24"
ip.data # => Bytes[172, 16, 10, 1]

It is usually used to include an IP address in a data packet to be sent over a socket

ip = IPAddress.new "10.1.1.0/24"
socket = Socket.new(params) # socket details here

# Send binary data
socket.send "Address: "
socket.send ip.data

[View source]
def each(&) : Nil #

Iterates over all the IP addresses for the given network (or IP address).

The object yielded is a new IPv4 object created from the iteration.

ip = IPAddress.new "10.0.0.1/29"
ip.each do |i|
  p i.address
end

# => "10.0.0.0"
# => "10.0.0.1"
# => "10.0.0.2"
# => "10.0.0.3"
# => "10.0.0.4"
# => "10.0.0.5"
# => "10.0.0.6"
# => "10.0.0.7"

[View source]
def each_host(&) : Nil #

Iterates over all the hosts IP addresses for the given network (or IP address).

ip = IPAddress.new "10.0.0.1/29"
ip.each_host do |i|
  p i.to_s
end

# => "10.0.0.1"
# => "10.0.0.2"
# => "10.0.0.3"
# => "10.0.0.4"
# => "10.0.0.5"
# => "10.0.0.6"

[View source]
def first : IPv4 #

Returns a new IPv4 object with the first host IP address in the range.

Example: given the 192.168.100.0/24 network, the first host IP address is 192.168.100.1.

ip = IPAddress.new "192.168.100.0/24"
ip.first.to_s # => "192.168.100.1"

The object IP doesn't need to be a network: the method automatically gets the network number from it

ip = IPAddress.new "192.168.100.50/24"
ip.first.to_s # => "192.168.100.1"

[View source]
def hash(hasher) #
Description copied from class Reference

See Object#hash(hasher)


def hosts : Array(IPv4) #

Returns an array with the IP addresses of all the hosts in the network.

ip = IPAddress.new "10.0.0.1/29"
ip.hosts.map &.address
# => ["10.0.0.1",
# => "10.0.0.2",
# => "10.0.0.3",
# => "10.0.0.4",
# => "10.0.0.5",
# => "10.0.0.6"]

[View source]
def includes?(others : Array(IPv4)) #

ditto


[View source]
def includes?(other) #

Checks whether a subnet includes the given IP address.

Accepts an IPAddress::IPv4 object.

ip = IPAddress.new "192.168.10.100/24"

addr1 = IPAddress.new "192.168.10.102/24"
addr2 = IPAddress.new "172.16.0.48/16"

ip.includes? addr1 # => true
ip.includes? addr2 # => false

[View source]
def includes?(*others : IPv4) #

Checks whether a subnet includes all the given IPv4 objects.

ip = IPAddress.new "192.168.10.100/24"

addr1 = IPAddress.new "192.168.10.102/24"
addr2 = IPAddress.new "192.168.10.103/24"

ip.includes? addr1, addr2 # => true

[View source]
def last : IPv4 #

Like its sibling method IPv4#first, this method returns a new IPv4 object with the last host IP address in the range.

Example: given the 192.168.100.0/24 network, the last host IP address is 192.168.100.254.

ip = IPAddress.new "192.168.100.0/24"
ip.last.to_s # => "192.168.100.254"

The object IP doesn't need to be a network: the method automatically gets the network number from it.

ip = IPAddress.new "192.168.100.50/24"
ip.last.to_s # => "192.168.100.254"

[View source]
def link_local? #

Checks if an IPv4 address objects belongs to a link-local network RFC3927.

ip = IPAddress.new "169.254.0.1"
ip.link_local? # => true

[View source]
def loopback? #

Checks if an IPv4 address objects belongs to a loopback network RFC1122.

ip = IPAddress.new "127.0.0.1"
ip.loopback? # => true

[View source]
def multicast? #

Checks if an IPv4 address objects belongs to a multicast network RFC3171.

ip = IPAddress.new "224.0.0.0/4"
ip.multicast? # => true

[View source]
def netmask : String #

Returns the prefix as a string in IP format.

ip = IPAddress.new "172.16.100.4/22"
ip.netmask # => "255.255.252.0"

[View source]
def netmask=(addr : String) : Nil #

Like IPv4#prefix=, this method allow you to change the prefix/netmask of an IP address object.

ip = IPAddress.new "172.16.100.4"
ip.to_string # => "172.16.100.4/16"

ip.netmask = "255.255.252.0"
ip.to_string # => "172.16.100.4/22"

[View source]
def network : IPv4 #

Returns a new IPv4 object with the network number for the given IP.

ip = IPAddress.new "172.16.10.64/24"
ip.network.to_s # => "172.16.10.0"

[View source]
def network? #

Checks if the IP address is actually a network.

ip = IPAddress.new "172.16.10.64/24"
ip.network? # => false

ip = IPAddress.new "172.16.10.64/26"
ip.network? # => true

[View source]
def network_u32 : UInt32 #

Returns the network number in unsigned 32 bits format.

ip = IPAddress.new "10.0.0.1/29"
ip.network_u32 # => 167772160

[View source]
def octets : Array(Int32) #

Returns the address as an Array of decimal values.

ip = IPAddress.new "172.16.100.4"
ip.octets # => [172, 16, 100, 4]

[View source]
def pred : IPv4 #

Returns the predecessor to the IP address.

ip = IPAddress.new "192.168.45.23/16"
ip.pred.to_string # => "192.168.45.22/16"

[View source]
def prefix : Prefix32 #

Returns the prefix portion of the IPv4 object as a IPAddress::Prefix32 object.

ip = IPAddress.new "172.16.100.4/22"

ip.prefix       # => 22
ip.prefix.class # => IPAddress::Prefix32

[View source]
def prefix=(prefix : Int32) : Prefix32 #

Set a new prefix number for the object.

This is useful if you want to change the prefix to an object created with IPv4.parse_u32 or if the object was created using the classful mask.

ip = IPAddress.new "172.16.100.4"
ip.to_string # => "172.16.100.4/16"

ip.prefix = 22
ip.to_string # => "172.16.100.4/22"

[View source]
def private? #

Checks if an IPv4 address objects belongs to a private network RFC1918.

ip = IPAddress.new "10.1.1.1/24"
ip.private? # => true

[View source]
def reverse : String #

Returns the IP address in in-addr.arpa format for DNS lookups.

ip = IPAddress.new "172.16.100.50/24"
ip.reverse # => "50.100.16.172.in-addr.arpa"

[View source]
def size : Int32 #

Returns the number of IP addresses included in the network. It also counts the network address and the broadcast address.

ip = IPAddress.new "10.0.0.1/29"
ip.size # => 8

[View source]
def split(subnets : Int32 = 2) : Array(IPv4) #

Splits a network into different subnets and returns an array of IPv4 objects.

If the IP Address is a network, it can be divided into multiple networks. If self is not a network, this method will calculate the network from the IP and then subnet it.

If subnets is an power of two number, the resulting networks will be divided evenly from the supernet.

network = IPAddress.new "172.16.10.0/24"

# implies .map &.to_string
network / 4
# => ["172.16.10.0/26",
# => "172.16.10.64/26",
# => "172.16.10.128/26",
# => "172.16.10.192/26"]

If num is any other number, the supernet will be divided into some networks with a even number of hosts and other networks with the remaining addresses.

network = IPAddress.new "172.16.10.0/24"

# implies .map &.to_string
network / 3
# => ["172.16.10.0/26",
# => "172.16.10.64/26",
# => "172.16.10.128/25"]

[View source]
def subnets(subprefix : Int32) : Array(IPv4) #

This method implements the subnetting function similar to the one described in RFC3531.

By specifying a new prefix, the method calculates the network number for the given IPv4 object and calculates the subnets associated to the new prefix.

For example, given the following network:

ip = IPAddress.new "172.16.10.0/24"

we can calculate the subnets with a /26 prefix

ip.subnets(26).map &.to_string
# => ["172.16.10.0/26", "172.16.10.64/26",
# => "172.16.10.128/26", "172.16.10.192/26"]

The resulting number of subnets will of course always be a power of two.


[View source]
def succ : IPv4 #

Returns the successor to the IP address.

ip = IPAddress.new "192.168.45.23/16"
ip.succ.to_string # => "192.168.45.24/16"

[View source]
def supernet(new_prefix : Int32) : IPv4 #

Returns a new IPv4 object from the supernetting of the instance network.

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.new "172.16.10.0/24"

you can supernet it with a new /23 prefix

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

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

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

NOTE If new_prefix is less than 1, returns 0.0.0.0/0.


[View source]
def to_hex : String #

Returns the address portion in hex.

ip = IPAddress.new "10.0.0.0"
ip.to_hex # => "0a000000"

[View source]
def to_ipv6 : String #

Returns the ip address in a format compatible with the IPv6 mapped IPv4 addresses.

ip = IPAddress.new "172.16.10.1/24"
ip.to_ipv6 # => "ac10:0a01"

[View source]
def to_s(io : IO) #

Appends a string with the address portion of the IPv4 object to the given IO object.

ip = IPAddress.new "172.16.100.4/22"
ip.to_s # => "172.16.100.4"

[View source]
def to_string : String #

Returns a string with the IP address in canonical form.

ip = IPAddress.new "172.16.100.4/22"
ip.to_string # => "172.16.100.4/22"

[View source]
def to_u32 : UInt32 #

Returns the address portion in unsigned 32 bits integer format.

This method is identical to the C function inet_pton to create a 32 bits address family structure.

ip = IPAddress.new "10.0.0.0/8"
ip.to_u32 # => 167772160

[View source]
def upto(limit : IPv4) : Array(IPv4) #

Returns a list of IP's between #address and the supplied IP.

ip = IPAddress.new "172.16.100.51/32"

# implies .map &.to_s
ip.upto "172.16.100.100"
# => ["172.16.100.51",
# => "172.16.100.52",
# => ...
# => "172.16.100.99",
# => "172.16.100.100"]

[View source]
def upto(limit : String) : Array(IPv4) #

ditto


[View source]