Skip to Main Content
  • Questions
  • Converting IP Range to CIDR in Plsql

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Vivek.

Asked: February 11, 2016 - 3:37 pm UTC

Last updated: May 13, 2019 - 4:28 am UTC

Version: Oracle 11g

Viewed 10K+ times! This question is

You Asked

Hi,

I wanted to convert a given IP range(Ex :10.81.162.0 - 10.81.163.51) to its CIDR (Ex :10.81.162.0/24,10.81.163.0/27,10.81.163.32/28,10.81.163.48/30 ) format using a plsql function. I found some java codes for it but didn't know how to implement it in plsql. As of now I am just using a temporary table to maintain the CIDR's of the provided Ranges and use it in my procedure. But it will be of great help if i can have a function to convert it on the fly.

Any help or redirection to any other link is appreciated.


Thanks
Vivek

and Connor said...

When in doubt... we can cheat :-) We just load the Java into the database and use it from there


SQL> CREATE OR REPLACE and COMPILE JAVA SOURCE
  2  NAMED "MyCommand"
  3  AS
  4  import java.io.*;
  5  import java.lang.*;
  6  import java.util.ArrayList;
  7  import java.util.List;
  8
  9
 10  public class MyCommand extends Object
 11  {
 12
 13      public static String RANGE2CIDRLIST( String startIp, String endIp ) {
 14          long start = ipToLong(startIp);
 15          long end = ipToLong(endIp);
 16
 17          String pairs = "";
 18
 19          while ( end >= start ) {
 20              byte maxsize = 32;
 21              while ( maxsize > 0) {
 22                  long mask = CIDR2MASK[ maxsize -1 ];
 23                  long maskedBase = start & mask;
 24
 25                  if ( maskedBase != start ) {
 26                      break;
 27                  }
 28
 29                  maxsize--;
 30              }
 31              double x = Math.log( end - start + 1) / Math.log( 2 );
 32              byte maxdiff = (byte)( 32 - Math.floor( x ) );
 33              if ( maxsize < maxdiff) {
 34                  maxsize = maxdiff;
 35              }
 36              String ip = longToIP(start);
 37              pairs = pairs +  ip + "/" + maxsize + ", ";
 38              start += Math.pow( 2, (32 - maxsize) );
 39          }
 40          return pairs;
 41      }
 42
 43      public static final int[] CIDR2MASK = new int[] { 0x00000000, 0x80000000,
 44          0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000,
 45          0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,
 46          0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
 47          0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800,
 48          0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,
 49          0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,
 50          0xFFFFFFFF };
 51
 52      private static long ipToLong(String strIP) {
 53          long[] ip = new long[4];
 54          String[] ipSec = strIP.split("\\.");
 55          for (int k = 0; k < 4; k++) {
 56              ip[k] = Long.valueOf(ipSec[k]);
 57          }
 58
 59          return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
 60      }
 61
 62      private static String longToIP(long longIP) {
 63          StringBuffer sb = new StringBuffer("");
 64          sb.append(String.valueOf(longIP >>> 24));
 65          sb.append(".");
 66          sb.append(String.valueOf((longIP & 0x00FFFFFF) >>> 16));
 67          sb.append(".");
 68          sb.append(String.valueOf((longIP & 0x0000FFFF) >>> 8));
 69          sb.append(".");
 70          sb.append(String.valueOf(longIP & 0x000000FF));
 71
 72          return sb.toString();
 73      }
 74
 75  }
 76  /

Java created.

SQL> sho err
No errors.
SQL>
SQL> create or replace function ip_to_cidr( p_ip1  in varchar2, p_ip2 in varchar2)
  2  return varchar2
  3  AS LANGUAGE JAVA NAME  'MyCommand.RANGE2CIDRLIST(java.lang.String, java.lang.String) return java.lang.String';
  4  /

Function created.

SQL> sho err
No errors.
SQL>
SQL> set serverout on
SQL> declare
  2    x varchar2(1000);
  3  begin
  4    x := ip_to_cidr('10.81.162.0','10.81.163.51');
  5    dbms_output.put_line(x);
  6  end;
  7  /
10.81.162.0/24, 10.81.163.0/27, 10.81.163.32/28, 10.81.163.48/30,

PL/SQL procedure successfully completed.

SQL>
SQL>


And please dont tell anyone I'm using Java... my peers will have me for dinner :-)

Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Vivek Das, February 12, 2016 - 1:19 pm UTC

Hi Connor,

Thanks for your reply !!

Actually i don't have Java installed and also can't ask for it :) for the same reason, people here will have me for dinner :(.

I tried to replicate the jave code provided in plsql. I could only convert ipToLong function. here is the plsql code :

create or replace function ip_to_long (Ip In varchar2) return clob
IS

type lob_array is varray(6) of clob;
--type string_array is varray(5) of varchar2(50);

lob_values lob_array;
--string_values string_array;
dot_count integer;
counter integer :=0;
begin
dot_count := regexp_count(Ip, '\.',1, 'i');
counter := counter + 1;

lob_values := lob_array();
lob_values.extend;
lob_values(counter) := to_clob(substr(ip,1, instr(ip, '.',1,1)-1));

for i in 1 .. dot_count -1
loop
counter := counter + 1;
lob_values.extend;
lob_values(counter) := to_clob(substr(ip,instr(ip,'.',1,i)+1,(instr(ip, '.',1,i+1)-1)- instr(ip,'.',1,i)));
end loop;

counter := counter + 1;
lob_values.extend;
lob_values(counter) := to_clob(substr(Ip,instr(IP,'.',1, 3)+1,length(IP)));

return to_clob((to_number(lob_values(1))*power(2,24)) + (to_number(lob_values(2))*power(2,16))+
(to_number(lob_values(3))*power(2,8))+(to_number(lob_values(4))));

end;


The other 2 functions LongTOip and RangeTOCIDRList uses some bitmasks I guess (0xF8000000, 0x00FFFFFF). I don't know how to use it in oracle. I can use the Bitwise & and OR operators, but dont know how to use those masked numbers which are also in the int array.

Can you please help in finding a way to use those bitmasks, so that we can replicate the other 2 functions also.

Regards,
Vivek




Connor McDonald
February 12, 2016 - 11:18 pm UTC

Java is already there within the database. You should be able to just cut/paste what I did.

That said - I'll have a play with a PLSQL version for fun, but I wouldnt wait for me :-)

Amine, February 12, 2016 - 1:48 pm UTC

Hi Vivek. You don't have to have Java installed. It already exists in your database. Connor wrote his code in sqlplus.

Vivek Das, February 13, 2016 - 10:36 am UTC

I am using 'Oracle Database 11g Enterprise Edition', so you are correct Jave should ideally exist in the DB. But i still get this error while running it in SQL Developer :

Error report:
ORA-29538: Java not installed
29538. 00000 - "Java not installed"

May be some Privileges are missing. I will check with the DBA and also try it through sqlplus.

Thanks :)

Vivek
Connor McDonald
February 14, 2016 - 4:07 am UTC

Its quite possible the DBA chose *not* to have the default Java option installed.

If I get a PLSQL version working, I'll post again.

Vivek Das, February 15, 2016 - 4:44 pm UTC

Hi Conner,

I derived a PLSQL version from the Java code you provided. Have verified it with the results of the Java code. Its working pretty fine :). Just pasting the code here if someone else might require.

----------------------------------Function IP_to_long1--------------------------

create or replace
function ip_to_long1 (Ip In varchar2) return decimal
IS

type lob_array is varray(6) of decimal;
lob_values lob_array;
dot_count integer;
counter integer :=0;
begin
dot_count := regexp_count(Ip, '\.',1, 'i');
counter := counter + 1;

lob_values := lob_array();
lob_values.extend;
lob_values(counter) := substr(ip,1, instr(ip, '.',1,1)-1);

for i in 1 .. dot_count -1
loop
counter := counter + 1;
lob_values.extend;
lob_values(counter) := substr(ip,instr(ip,'.',1,i)+1,(instr(ip, '.',1,i+1)-1)- instr(ip,'.',1,i));
end loop;

counter := counter + 1;
lob_values.extend;
lob_values(counter) := substr(Ip,instr(IP,'.',1, 3)+1,length(IP));

return (lob_values(1)*power(2,24)) + (lob_values(2)*power(2,16))+
(lob_values(3)*power(2,8))+(lob_values(4));

end;


-----------------------------------Function LongToIP1-----------------------------

create or replace
function longToIP1 (longIP in decimal) return varchar2
is

sb varchar2(4000);

begin

sb := to_char(floor(longip/power(2,24)) || '.' || floor((bitand(longIP,16777215)/ power(2,16))) || '.' || floor((bitand(longIP,65535)/ power(2,8)))
|| '.' || floor((bitand(longIP,255))));

return sb;

end;

-----------------------------Function Range_to_Cidr2------------------------------------


create or replace
function range_to_cidr2 (startIp In varchar2, endIp In varchar2) return clob
is

type conv is varray(33) of integer;
cidrtomask conv;
long_start integer;
long_end integer;
mask integer;
maskedbase integer;
maxsize integer;
x number(5,3);
maxdiff integer;
ip clob;
pairs clob := '';
break_count integer := 0;
begin


cidrtomask := conv(0, 2147483648,
3221225472, 3758096384, 4026531840, 4160749568, 4227858432,
4261412864, 4278190080, 4286578688, 4290772992, 4292870144,
4293918720, 4294443008, 4294705152, 4294836224, 4294901760,
4294934528, 4294950912, 4294959104, 4294963200, 4294965248,
4294966272, 4294966784, 4294967040, 4294967168, 4294967232,
4294967264, 4294967280, 4294967288, 4294967292, 4294967294,
4294967295);



long_start := ip_to_long1(StartIP);
long_end := ip_to_long1(EndIp);

while long_end >= long_start
loop
maxsize :=0;
x := ln((long_end-long_start) +1)/ ln(2);
maxdiff := (32- floor(x));
if maxsize < maxdiff then
maxsize := maxdiff;
end if;
ip := to_clob(longtoip1(Long_start));
pairs := Pairs || ip || '/' || to_clob(maxsize) || ',';
long_start := long_start+(power(2, (32-maxsize)));
end loop;

return pairs;

end;

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


Vivek

Vivek Das, February 16, 2016 - 2:25 pm UTC

The above code I had pasted dosent work for all ranges. The below code is the correct one and should work for all the ip range to cidr conversion .

----------------------------------Function IP_to_long1--------------------------

create or replace
function ip_to_long1 (Ip In varchar2) return decimal
IS

type lob_array is varray(6) of decimal;
lob_values lob_array;
dot_count integer;
counter integer :=0;
begin
dot_count := regexp_count(Ip, '\.',1, 'i');
counter := counter + 1;

lob_values := lob_array();
lob_values.extend;
lob_values(counter) := substr(ip,1, instr(ip, '.',1,1)-1);

for i in 1 .. dot_count -1
loop
counter := counter + 1;
lob_values.extend;
lob_values(counter) := substr(ip,instr(ip,'.',1,i)+1,(instr(ip, '.',1,i+1)-1)- instr(ip,'.',1,i));
end loop;

counter := counter + 1;
lob_values.extend;
lob_values(counter) := substr(Ip,instr(IP,'.',1, 3)+1,length(IP));

return (lob_values(1)*power(2,24)) + (lob_values(2)*power(2,16))+
(lob_values(3)*power(2,8))+(lob_values(4));

end;


-----------------------------------Function LongToIP1-----------------------------

create or replace
function longToIP1 (longIP in decimal) return varchar2
is

sb varchar2(4000);

begin

sb := to_char(floor(longip/power(2,24)) || '.' || floor((bitand(longIP,16777215)/ power(2,16))) || '.' || floor((bitand(longIP,65535)/ power(2,8)))
|| '.' || floor((bitand(longIP,255))));

return sb;

end;

-----------------------------Function Range_to_Cidr2------------------------------------


create or replace
function range_to_cidr2 (startIp In varchar2, endIp In varchar2) return clob
is
type conv is varray(33) of integer;
cidrtomask conv;
long_start integer;
long_end integer;
mask integer;
maskedbase integer;
maxsize integer;
x number(5,3);
maxdiff integer;
ip clob;
pairs clob := '';
break_count integer := 0;
begin


cidrtomask := conv(0, 2147483648,
3221225472, 3758096384, 4026531840, 4160749568, 4227858432,
4261412864, 4278190080, 4286578688, 4290772992, 4292870144,
4293918720, 4294443008, 4294705152, 4294836224, 4294901760,
4294934528, 4294950912, 4294959104, 4294963200, 4294965248,
4294966272, 4294966784, 4294967040, 4294967168, 4294967232,
4294967264, 4294967280, 4294967288, 4294967292, 4294967294,
4294967295);



long_start := ip_to_long1(StartIP);
long_end := ip_to_long1(EndIp);

while long_end >= long_start

loop
maxsize :=33;
while maxsize > 1
loop
mask := cidrtomask(maxsize -1);

maskedbase := bitand(long_start,mask);
if maskedbase != long_start then
goto next_step;
end if;

maxsize := maxsize-1;
end loop;
<<next_step>>
x := ln((long_end-long_start) +1)/ ln(2);
maxdiff := (32- floor(x));
if (maxsize-1) < maxdiff then
ip := to_clob(longtoip1(Long_start));
pairs := Pairs || ip || '/' || to_clob(maxdiff) || ', ';
long_start := long_start+(power(2, (32-(maxdiff))));
else
ip := to_clob(longtoip1(Long_start));
pairs := Pairs || ip || '/' || to_clob(maxsize-1) || ', ';
long_start := long_start+(power(2, (32-(maxsize-1))));
end if;

end loop;

return pairs;

end;

----------------------------------------------------------------------------------------
Chris Saxon
February 16, 2016 - 2:54 pm UTC

Thanks for the update.

The opposite way?

Mihai, May 07, 2019 - 2:00 pm UTC

Hi guys,
Can you post an example of the opposite? I need to convert CIDR to IP Range.

Having as input param this string '10.188.57.192/26' I need the result
IP address range: 10.188.57.192-10.188.57.255

or just the list:
10.188.57.192
10.188.57.193
...
10.188.57.252
10.188.57.253
10.188.57.254
10.188.57.255

Connor McDonald
May 13, 2019 - 4:28 am UTC

I built a little utility package (see https://connor-mcdonald.com/2018/01/23/dealing-with-ip-addresses/ ) and extended that

SQL> create or replace
  2  package ip_util is
  3
  4  function ip_num_from_str(p_ip_str varchar2) return number deterministic;
  5  function ip_str_from_num(p_ipnum number) return varchar2 deterministic;
  6  function to_num( p_bin in varchar2 ) return number deterministic;
  7  function to_bin( p_dec in number ) return varchar2;
  8
  9  end;
 10  /

Package created.

SQL>
SQL> create or replace
  2  package body ip_util is
  3
  4    l_ip41 constant number(12)  := 256;        -- power(256,1);
  5    l_ip42 constant number(12)  := 65536;      -- power(256,2);
  6    l_ip43 constant number(12)  := 16777216;   -- power(256,3);
  7    l_ip44 constant number(12)  := 4294967296; -- power(256,4);
  8
  9  function ip_num_from_str(p_ip_str varchar2) return number deterministic is
 10    l_ip_num     number;
 11    l_dot1       pls_integer;
 12    l_dot2       pls_integer;
 13    l_dot3       pls_integer;
 14    l_dot4       pls_integer;
 15
 16    l_colon      pls_integer;
 17    l_colon_cnt  pls_integer;
 18    l_hex        varchar2(32);
 19    l_ip_str     varchar2(64);
 20  begin
 21      l_dot1   := instr(p_ip_str,'.');
 22      l_dot2   := instr(p_ip_str,'.',l_dot1+1);
 23      l_dot3   := instr(p_ip_str,'.',l_dot2+1);
 24      l_dot4   := instr(p_ip_str,'.',l_dot3+1);
 25
 26      l_ip_num :=  l_ip43*to_number(substr(p_ip_str,1,l_dot1-1)) +
 27                   l_ip42*to_number(substr(p_ip_str,l_dot1+1,l_dot2-l_dot1-1)) +
 28                   l_ip41*to_number(substr(p_ip_str,l_dot2+1,l_dot3-l_dot2-1)) +
 29                   to_number(substr(p_ip_str,l_dot3+1));
 30
 31    return l_ip_num;
 32  end;
 33
 34  function ip_str_from_num(p_ipnum number) return varchar2 deterministic is
 35  begin
 36      return  mod(trunc(p_ipnum/l_ip43),l_ip41) ||'.'||
 37              mod(trunc(p_ipnum/l_ip42),l_ip41) ||'.'||
 38              mod(trunc(p_ipnum/l_ip41),l_ip41) ||'.'||
 39              mod(p_ipnum,l_ip41);
 40
 41  end;
 42
 43  function to_num( p_bin in varchar2 ) return number is
 44    l_num int := 0;
 45    l_power int := 1;
 46  begin
 47    for i in reverse 1 .. length(p_bin)  loop
 48      l_num := l_num + to_number(substr(p_bin,i,1))*l_power;
 49      l_power := l_power*2;
 50    end loop;
 51    return l_num;
 52  end;
 53
 54  function to_bin( p_dec in number ) return varchar2
 55  is
 56     l_str varchar2(255) default NULL;
 57     l_num number default p_dec;
 58     l_bit varchar2(16) default '01';
 59  begin
 60     loop
 61      l_str := substr( l_bit, mod(l_num,2)+1, 1 ) || l_str;
 62      l_num := trunc( l_num/2 );
 63      exit when ( l_num = 0 );
 64     end loop;
 65     return l_str;
 66  end;
 67
 68  end;
 69  /

Package body created.

SQL>
SQL>
SQL> with t as
  2   ( select '10.188.57.193' ip, 26 subnet from dual )
  3  select
  4    ip_util.ip_str_from_num(
  5       bitand(
  6         ip_util.ip_num_from_str(ip) ,
  7         ip_util.to_num(substr(rpad('1',subnet,'1')||rpad('0',32,'0'),1,32))
  8       )
  9     ) i1,
 10    ip_util.ip_str_from_num(
 11       ip_util.to_num(
 12         substr(
 13           substr(
 14             lpad(ip_util.to_bin(ip_util.ip_num_from_str(ip)),32,'0'),1,subnet)
 15               ||rpad('1',32,'1'),1,32)
 16          )
 17    ) i2
 18  from t;

I1                             I2
------------------------------ ------------------------------
10.188.57.192                  10.188.57.255

1 row selected.

SQL>


More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library