net_utils.pm 11 KB

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