loading...

Daily Challenge #31 - Count IPv4 Addresses

thepracticaldev profile image dev.to staff ・1 min read

Your challenge is to write a function that accepts starting and ending IPv4 addresses and returns the number of IP addresses from start to end, excluding the ending address. All input will be valid IPv4 addresses in the forms of strings.

Examples:
ipsBetween("10.0.0.0", "10.0.0.50") => 50
ipsBetween("10.0.0.0", "10.0.1.0") => 256
ipsBetween("20.0.0.10", "20.0.1.0") => 246

It would be impressive if anyone wanted to try their hand at creating the same function for IPv6. You might have to work with some hefty numbers though.

Good luck, happy coding!


This challenge comes from user xDranik. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

pic
Editor guide
 

Ruby, and totally cheating by using it's IPAddr class :)

def ips_between(from, to)
  IPAddr.new(to).to_i - IPAddr.new(from).to_i
end
 

This actually made me chuckle... lol cheating indeed 😁

 

And it was my first idea as well... IPv4 Addresses are 4byte integers... so the easiest way is to subtract them. 😎

 
def ip2int ip
  ip.split(".").map(&:to_i).pack('CCCC').unpack('N')[0]
end

def ipsBetween start, end_
  ip2int(end_) - ip2int(start)
end

p ipsBetween("10.0.0.0", "10.0.0.50") # => 50
p ipsBetween("10.0.0.0", "10.0.1.0")  # => 256
p ipsBetween("20.0.0.10", "20.0.1.0") # => 246

ip2int() is from Stack Overflow.

 

Here's mine! I extended it a little to work if the start and end were reversed.

const convertIPStringToNumber = ipString => ipString.split(".")
    .reduce((acc, part) => (acc << 8) | Number(part), 0);

const ipsBetween = (start, finish) => 
    Math.abs(convertIPStringToNumber(finish) - convertIPStringToNumber(start));

Gist: gist.github.com/kerrishotts/797450...

 

Perl, using recursion. Tests included. It throws an exception if the end address precedes the start one.

#!/usr/bin/perl
use warnings;
use strict;

sub _ips_between {
    my ($list1, $list2, $sum) = @_;
    return $sum unless @$list1;

    my ($e1, $e2) = (shift @$list1, shift @$list2);
    die "End before start.\n" if $e2 < $e1 && $sum == 0;

    return _ips_between($list1, $list2, $sum + ($e2 - $e1) * 256 ** @$list1)
}

sub ips_between {
    _ips_between([split /\./, shift],
                 [split /\./, shift],
                 0)
}

use Test::More tests => 6;
use Test::Exception;

is ips_between('10.0.0.0', '10.0.0.50'), 50;
is ips_between('10.0.0.0', '10.0.1.0'), 256;
is ips_between('20.0.0.10', '20.0.1.0'), 246;
is ips_between('0.255.127.12', '1.2.3.4'), 164_856;
is ips_between('1.1.1.1', '1.1.1.1'), 0;
throws_ok { ips_between('1.1.1.1', '1.1.0.1') } qr/End before start/;
 

Python IPv4 and IPv6:

def ipsBetween(s,e): #Ipv4
    if s == e : return 0
    s,e,n=s.split('.'),e.split('.'),0
    for i in range(len(s)):
        if s[i]  < e[i]:    n+=int(e[i])
        elif s[i] == e[i] and n>0:  n+=255
        elif s[i]>e[i]: n+=255-int(s[i])
    return n
def ipsBetweenIPv6(s,e): #Ipv6
    s= [int(i,16) for i in s.split(':')]
    e= [int(i,16) for i in e.split(':')]
    return sum(map(int,e))- sum(map(int,s))

print(ipsBetween("10.0.0.0", "10.0.0.50"))
print(ipsBetween("10.0.0.0", "10.0.1.0"))
print(ipsBetween("20.0.0.10", "20.0.1.0"))  
print(ipsBetweenIPv6('2001:db8:85a3:8d3:1319:8a2e:370:7348','2001:db8:85a3:8d3:1319:8a2e:3a0:7348')) #48
 

Elixir:

defmodule IP do
  import Bitwise

  def between(from, to),
    do: abs(to_integer(to) - to_integer(from))

  defp to_integer(ip) do
    ip
    |> String.split(".")
    |> Enum.reduce(0, fn part, acc -> acc <<< 8 ||| String.to_integer(part) end)
  end
end
 

JavaScript

const ipsBetween = (ip1, ip2) => {
  let diff = 0;
  const aIp1 = ip1.split(".");
  const aIp2 = ip2.split(".");

  // check that the IPs are well formed
  if (aIp1.length !== 4 || aIp2.length !== 4) {
    return "Invalid IPs: incorrect format";
  }

  for (x = 0; x < 4; x++) {
    // check that all the parts are valid (numeric and 0-255)
    if (
      isNaN(aIp1[x]) || isNaN(aIp2[x]) 
      || aIp1[x] < 0 || aIp1[x] > 255
      || aIp2[x] < 0 || aIp2[x] > 255
    ) {
      return "Invalid IPs: incorrect values"
    }
    diff += (aIp1[x] - aIp2[x]) * (256 ** (3-x));
  }
  return Math.abs(diff);
}

A bit verbose, but it seems to work fine. Live demo on CodePen.

 
// sectionSum :: (Number -> Number) -> Number
const sectionSum = value => section => value * 256 ** section;
// ipSum :: [Number] -> Number
const ipSum = ipArray => ipArray.reduceRight((sum, value, section) => sum += sectionSum(value)(3-section));
// ipDifference :: Number -> Number -> Number
const ipDifference = ipSum1 => ipSum2 => Math.abs(ipSum1 - ipSum2);
// ipSections :: String -> [Number]
const ipSections = address => address.split('.').map(section => parseInt(section));
// ipVal :: String -> Number 
const ipVal = ip => ipSum(ipSections(ip))

// ipsBetween :: (String -> String) -> Number 
const ipsBetweenCount = startIp => endIp => ipDifference(ipVal(startIp))(ipVal(endIp))
 

Here is the simple solution with Python:

def ips_between(start, end):
    start_ip_array = start.split('.')
    end_ip_array = end.split('.')

    first_part = int(end_ip_array[3]) - int(start_ip_array[3])
    second_part = (int(end_ip_array[2]) - int(start_ip_array[2])) * 256
    third_part = (int(end_ip_array[1]) - int(start_ip_array[1])) * pow(256, 2)
    end_part = (int(end_ip_array[0]) - int(start_ip_array[0])) * pow(256, 3)

    return sum([first_part, second_part, third_part, end_part])
 

PHP :D

function ipsBetween($from, $to)
{
    return ip2long($to) - ip2long($from);    
}

demo: 3v4l.org/8NiWc

 

in C#

public long IPAddressesBetween(string start, string end) => 
    IPAddress.Parse(end).Address - IPAddress.Parse(start).Address;