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
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
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;
----------------------------------------------------------------------------------------
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
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>