1
0

net_utils.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. package Rstat::net_utils;
  2. #
  3. # Copyright (C) Roman Dmitiriev, rnd@rajven.ru
  4. #
  5. use utf8;
  6. use strict;
  7. use English;
  8. use FindBin qw($Bin);
  9. use lib "$Bin";
  10. use base 'Exporter';
  11. use vars qw(@EXPORT @ISA);
  12. use FileHandle;
  13. use POSIX;
  14. use Rstat::config;
  15. use Rstat::main;
  16. use Net::Ping;
  17. use Net::Patricia;
  18. use NetAddr::IP;
  19. use Net::DNS;
  20. our @ISA = qw(Exporter);
  21. our @EXPORT = qw(
  22. $RFC1918
  23. $Special_Nets
  24. $Loopback
  25. IPv4Numeric
  26. is_gate_valid
  27. CheckIP
  28. GetDhcpRange
  29. GetIpRange
  30. GetIP
  31. GetSubNet
  32. is_ip
  33. is_ipip_valid
  34. is_ip_valid
  35. print_net
  36. ping
  37. HostIsLive
  38. InitSubnets
  39. mac_simplify
  40. isc_mac_simplify
  41. mac_cisco
  42. mac2dec
  43. mac_splitted
  44. ResolveNames
  45. );
  46. BEGIN
  47. {
  48. #local nets
  49. our $RFC1918;
  50. our $Special_Nets;
  51. our $Loopback;
  52. #------------------------------------------------------------------------------------------------------------
  53. sub ResolveNames {
  54. my $hostname = shift;
  55. my $ns_ip = shift;
  56. my @result=();
  57. my $res = Net::DNS::Resolver->new;
  58. $res->nameservers($ns_ip) if ($ns_ip);
  59. my $query = $res->search($hostname,"A");
  60. my $result;
  61. if ($query) {
  62. foreach my $rr ($query->answer) { push(@result,$result = $rr->address); }
  63. }
  64. return (@result);
  65. }
  66. #------------------------------------------------------------------------------------------------------------
  67. sub IPv4Numeric {
  68. my $ip = shift;
  69. return 0 if (!$ip);
  70. my $net=NetAddr::IP->new($ip);
  71. return $net->numeric();
  72. }
  73. #------------------------------------------------------------------------------------------------------------
  74. sub is_gate_valid {
  75. my $ip_str = trim($_[0]);
  76. if ($ip_str =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  77. my $mask = $5;
  78. return 0 if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  79. $mask =~s/\/// if ($mask);
  80. $mask = 32 if (!$mask);
  81. return 0 if ($mask > 31);
  82. if ($Special_Nets->match_string($ip_str)) { log_error("$ip_str in illegal net range"); return 0; };
  83. return 1;
  84. }
  85. return 0;
  86. }
  87. #---------------------------------------------------------------------------------------------------------
  88. sub CheckIP {
  89. my $ip_str = shift;
  90. return 0 if (!$ip_str);
  91. $ip_str = trim($ip_str);
  92. if ($ip_str =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  93. my $mask = $5;
  94. return 0 if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  95. $mask =~s/\/// if ($mask);
  96. $mask = 32 if (!$mask);
  97. my $ip = NetAddr::IP->new($ip_str)->addr();
  98. if ($mask<32) { $ip = $ip."/".$mask; }
  99. return $ip;
  100. }
  101. return 0;
  102. }
  103. #--------------------------------------------------------------------------------------------------------
  104. sub GetDhcpRange {
  105. my $gate_ip = trim($_[0]);
  106. my $mask;
  107. if ($gate_ip =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  108. $mask = $5;
  109. return if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  110. $mask =~s/\/// if ($mask);
  111. $mask = 32 if ((!$mask) or ($mask >32));
  112. } else { return; }
  113. my $gate = NetAddr::IP->new($gate_ip)->addr();
  114. my $net=NetAddr::IP->new($gate."/".$mask);
  115. my %dhcp;
  116. $dhcp{gate} = $gate;
  117. $dhcp{network} = $net->network()->addr();
  118. $dhcp{broadcast} = $net->broadcast()->addr();
  119. $dhcp{mask} = $net->mask();
  120. $dhcp{masklen} = $net->masklen();
  121. $dhcp{first_ip} = $net->first()->addr();
  122. $dhcp{count} = $net->broadcast() - $net->network() - 2;
  123. if ($mask < 32) {
  124. if ($dhcp{first_ip} eq $dhcp{gate}) {
  125. $dhcp{first_ip} = $net->nth(1)->addr();
  126. }
  127. $dhcp{last_ip} = $net->last()->addr();
  128. if ($dhcp{last_ip} eq $dhcp{gate}) {
  129. $dhcp{last_ip} = $net->nth($dhcp{count}-1)->addr();
  130. }
  131. }
  132. return \%dhcp;
  133. }
  134. #--------------------------------------------------------------------------------------------------------
  135. sub GetIpRange {
  136. my $gate_ip = trim($_[0]);
  137. my $mask = 30;
  138. if ($gate_ip =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  139. $mask = $5;
  140. return if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  141. $mask =~s/\/// if ($mask);
  142. $mask = 30 if ((!$mask) or ($mask >30));
  143. } else { return; }
  144. my $gate = NetAddr::IP->new($gate_ip)->addr();
  145. my $net=NetAddr::IP->new($gate."/".$mask);
  146. my @range=();
  147. if ($mask >=29) {
  148. my $ip_count = $net->broadcast() - $net->network() - 2;
  149. for (my $index = 1; $index<=$ip_count; $index++) {
  150. my $ip = $net->nth($index)->addr();
  151. next if ($ip eq $gate);
  152. push(@range,$ip."/32");
  153. }
  154. } else {
  155. push(@range,$net->network()->addr()."/".$mask);
  156. }
  157. return \@range;
  158. }
  159. #--------------------------------------------------------------------------------------------------------
  160. sub GetIP {
  161. my $ip_str = trim($_[0]);
  162. if ($ip_str =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  163. return if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  164. return NetAddr::IP->new($ip_str)->addr();
  165. }
  166. return;
  167. }
  168. #---------------------------------------------------------------------------------------------------------
  169. sub GetSubNet {
  170. my $ip_str = trim($_[0]);
  171. my $mask = 32;
  172. if ($ip_str =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  173. $mask = $5;
  174. return if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  175. $mask =~s/\/// if ($mask);
  176. $mask = 32 if (!$mask);
  177. }
  178. else { return; }
  179. my $ip = NetAddr::IP->new($ip_str)->network()->addr();
  180. return $ip."/".$mask;
  181. }
  182. #---------------------------------------------------------------------------------------------------------
  183. sub is_ipip_valid {
  184. my $ip1 = shift;
  185. my $ip2 = shift;
  186. my $lo1 = 0;
  187. my $lo2 = 0;
  188. my $ok = 0;
  189. eval {
  190. if ($ip1) {
  191. if ($ip1 ne "0/0") {
  192. $lo1 = $Loopback->match_string($ip1);
  193. $lo1 = 1 if ($lo1);
  194. }
  195. };
  196. if ($ip2) {
  197. if ($ip2 ne "0/0") {
  198. $lo2 = $Loopback->match_string($ip1);
  199. $lo2 = 1 if ($lo2);
  200. }
  201. };
  202. if (!$lo1) { $lo1=0; };
  203. if (!$lo2) { $lo2=0; };
  204. $ok = ((($ip1 ne "0/0") or ($ip2 ne "0/0")) and ($lo1==0) and ($lo2==0));
  205. };
  206. return $ok;
  207. }
  208. #---------------------------------------------------------------------------------------------------------
  209. sub is_ip {
  210. my $ip_str = trim($_[0]);
  211. return 1 if (!$ip_str);
  212. return 1 if ($ip_str eq '0/0');
  213. if ($ip_str =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  214. my $mask = $5;
  215. return 0 if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  216. $mask =~s/\/// if ($mask);
  217. $mask = 32 if (!$mask);
  218. return 0 if ($mask > 32);
  219. return 1;
  220. }
  221. return 0;
  222. }
  223. #---------------------------------------------------------------------------------------------------------
  224. sub is_ip_valid {
  225. my $ip_str = trim($_[0]);
  226. if ($ip_str =~ /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/[0-9]{1,2}){0,1}/) {
  227. my $mask = $5;
  228. return 0 if($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
  229. $mask =~s/\/// if ($mask);
  230. $mask = 32 if (!$mask);
  231. return 0 if ($mask > 32);
  232. if ($Special_Nets->match_string($ip_str)) { log_error("$ip_str in illegal net range"); return 0; };
  233. return 1;
  234. }
  235. return 0;
  236. }
  237. #---------------------------------------------------------------------------------------------------------
  238. sub print_net {
  239. my $ip = shift;
  240. my $max_mask = shift || 26;
  241. return if (!$ip);
  242. my @result = ();
  243. my $user_ip=NetAddr::IP->new($ip);
  244. my $netmask = $user_ip->masklen();
  245. return if ($netmask<$max_mask);
  246. my $ip_first = $user_ip->network();
  247. my $ip_last = $user_ip->broadcast();
  248. my $ip_count = $ip_last - $ip_first;
  249. if ($ip_count) {
  250. for (my $i=0; $i<=$ip_count; $i++) {
  251. my $ip1 = $ip_first;
  252. $ip1=~s/\/\d+$//g;
  253. push(@result,$ip1);
  254. $ip_first ++;
  255. }
  256. } else {
  257. $ip_first=~s/\/\d+$//g;
  258. push(@result,$ip_first);
  259. }
  260. return @result;
  261. }
  262. #---------------------------------------------------------------------------------------------------------
  263. sub ping {
  264. use Net::Ping;
  265. use Time::HiRes;
  266. my ($host,$time) = @_;
  267. my $p = Net::Ping->new();
  268. $p->hires();
  269. my ($ret, $duration, $ip) = $p->ping($host, $time);
  270. $p->close();
  271. $ret ? return 1: return 0;
  272. }
  273. #---------------------------------------------------------------------------------------------------------
  274. sub HostIsLive {
  275. my $host=shift;
  276. my $proto=shift || "tcp";
  277. if ($< eq 0) { $proto="icmp"; }
  278. my $p = Net::Ping->new($proto);
  279. my $ok= $p->ping($host,5);
  280. $p->close();
  281. return $ok;
  282. }
  283. #----------------------------------------------------------------------------------
  284. sub InitSubnets {
  285. $RFC1918 = new Net::Patricia;
  286. #local nets RFC1918
  287. $RFC1918->add_string("192.168.0.0/16");
  288. $RFC1918->add_string("10.0.0.0/8");
  289. $RFC1918->add_string("172.16.0.0/12");
  290. #----------------------------------------------------------------------------------
  291. $Special_Nets = new Net::Patricia;
  292. #"This" Network [RFC1700, page 4]
  293. $Special_Nets->add_string("0.0.0.0/8");
  294. #Public-Data Networks [RFC1700, page 181]
  295. #$Special_Nets->add_string("14.0.0.0/8");
  296. #Cable Television Networks
  297. #$Special_Nets->add_string("24.0.0.0/8");
  298. #Reserved - [RFC1797]
  299. #$Special_Nets->add_string("39.0.0.0/8");
  300. #loopback [RFC1700, page 5]
  301. $Special_Nets->add_string("127.0.0.0/8");
  302. #Reserved
  303. $Special_Nets->add_string("128.0.0.0/16");
  304. #Link Local
  305. $Special_Nets->add_string("169.254.0.0/16");
  306. #Reserved
  307. #$Special_Nets->add_string("191.255.0.0/16");
  308. #Reserved
  309. #$Special_Nets->add_string("192.0.0.0/24");
  310. #Test-Net
  311. #$Special_Nets->add_string("192.0.2.0/24");
  312. #6to4 Relay Anycast [RFC3068]
  313. $Special_Nets->add_string("192.88.99.0/24");
  314. #Network Interconnect Device Benchmark Testing [RFC2544]
  315. #$Special_Nets->add_string("198.18.0.0/15");
  316. #Reserved
  317. #$Special_Nets->add_string("223.255.255.0/24");
  318. #Multicast [RFC3171]
  319. $Special_Nets->add_string("224.0.0.0/4");
  320. #Reserved for Future Use [RFC1700, page 4]
  321. #$Special_Nets->add_string("240.0.0.0/4");
  322. #----------------------------------------------------------------------------------
  323. $Loopback = new Net::Patricia;
  324. #loopback [RFC1700, page 5]
  325. $Loopback->add_string("127.0.0.0/8");
  326. }
  327. #--------------------------------- Utils ------------------------------------------
  328. sub mac_simplify{
  329. my $mac=shift;
  330. return if (!$mac);
  331. $mac=~s/\.//g;
  332. $mac=~s/://g;
  333. $mac=~s/-//g;
  334. $mac=~tr/[A-Z]/[a-z]/;
  335. return $mac;
  336. }
  337. #--------------------------------------------------------------------------------
  338. sub mac_cisco{
  339. my $mac=shift;
  340. return if (!$mac);
  341. $mac=mac_simplify($mac);
  342. $mac=~s/(\S{4})(\S{4})(\S{4})/$1\.$2\.$3/g;
  343. return $mac;
  344. }
  345. #--------------------------------------------------------------------------------
  346. sub mac2dec{
  347. my $mac=shift;
  348. return if (!$mac);
  349. $mac=mac_simplify($mac);
  350. $mac=mac_splitted($mac);
  351. my @m=split(":",$mac);
  352. $mac="";
  353. foreach my $i (@m) { $mac=$mac.".".hex($i); }
  354. $mac=~s/^\.//;
  355. return $mac;
  356. }
  357. #--------------------------------------------------------------------------------
  358. sub isc_mac_simplify{
  359. my $mac=shift;
  360. return if (!$mac);
  361. my @mac_array;
  362. foreach my $octet (split(/\:/,$mac)){
  363. my $dec=hex($octet);
  364. push(@mac_array,sprintf("%02x",$dec));
  365. }
  366. $mac=join('',@mac_array);
  367. return $mac;
  368. }
  369. #--------------------------------------------------------------------------------
  370. sub mac_splitted{
  371. my $mac=shift;
  372. return if (!$mac);
  373. my $ch=shift || ":";
  374. $mac=mac_simplify($mac);
  375. $mac=~s/(\S{2})(\S{2})(\S{2})(\S{2})(\S{2})(\S{2})/$1:$2:$3:$4:$5:$6/g;
  376. if ($ch ne ":") { $mac=~s/\:/$ch/g; }
  377. return $mac;
  378. }
  379. InitSubnets();
  380. 1;
  381. }