net_utils.pm 13 KB

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