ljr/local/cgi-bin/Golem/netlib.pl

331 lines
6.9 KiB
Perl

#!/usr/bin/perl -w
#
# networking related routines
#
package Golem;
use strict;
use Golem;
sub get_external_ipinfo {
my ($ip) = @_;
return undef unless $ip && $Golem::noc_nets_cgi;
my $ipinfo;
my @net_info;
my $wget_options = "";
$wget_options = "--timeout=${Golem::noc_nets_cgi_timeout}"
if $Golem::noc_nets_cgi_timeout;
open(NOCNET,"wget $wget_options -q -O - ${Golem::noc_nets_cgi}?name=$ip |");
Golem::debug("wget $wget_options -q -O - ${Golem::noc_nets_cgi}?name=$ip |");
my $line;
while($line=<NOCNET>) {
if ($line=~/^<tr align=center>/) {
chomp($line);
$line =~ s/<\/td><td>/\|/g;
$line =~ s/(<td>|<tr align=center>|<\/tr>|<\/td>)//g;
@net_info= split(/\|/,$line,5);
}
}
close(NOCNET);
if (@net_info) {
$ipinfo->{'ip'} = $net_info[0];
$ipinfo->{'subnet'} = $net_info[1];
$ipinfo->{'router'} = $net_info[2];
$ipinfo->{'iface'} = $net_info[3];
$ipinfo->{'vlan'} = $ipinfo->{'iface'};
$ipinfo->{'vlan'} =~ /(\d+)/;
$ipinfo->{'vlan'} = $1;
$ipinfo->{'router_if_addr'} = $net_info[4];
}
return $ipinfo;
}
# --- ghetto code
sub int2maskpart {
my ($i) = @_;
return "0" unless defined($i);
my $j = 0;
my $bits = "";
while ($j < 8) {
if ($i <= $j) {
$bits .= "0";
}
else {
$bits .= "1";
}
$j++;
}
return oct("0b" . $bits);
}
sub mask2netmask {
my ($j) = @_;
my @ip;
my $i;
for ($i = 1; $i <= int($j / 8); $i++) {
push @ip, int2maskpart(8);
}
while ($i < 5) {
push @ip, int2maskpart($j % 8);
$j = 0;
$i++;
}
return join(".", @ip);
}
# convert string representation of ipv4 address into integer
sub ipv4_str2int {
my ($ip_string) = @_;
if ($ip_string =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
return (16777216 * $1) + (65536 * $2) + (256 * $3) + $4;
}
else {
return 0;
}
}
# convert integer representation of ipv4 address into string
sub ipv4_int2str {
my ($ip_int) = @_;
if ($ip_int >= 0 && $ip_int <= 4294967295) {
my $w = ($ip_int / 16777216) % 256;
my $x = ($ip_int / 65536) % 256;
my $y = ($ip_int / 256) % 256;
my $z = $ip_int % 256;
return $w . "." . $x . "." . $y . "." . $z;
}
else {
return 0;
}
}
# /24 -> +255
# /27 -> +31
# /28 -> +15
# /29 -> +7
sub ipv4_mask2offset {
my ($mask) = @_;
$mask ||= 0;
my $offset = 2 ** (32 - $mask);
return $offset - 1;
}
sub get_net {
my ($ip, $mask, $opts) = @_;
Golem::trim(\$ip);
Golem::die("Programmer error: get_net expects net and mask")
unless
($ip && $mask && Golem::is_digital($mask)) ||
($ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/);
if ($ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/o) {
$ip = ipv4_str2int($1);
$mask = $2;
}
elsif ($ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/o) {
$ip = ipv4_str2int($1);
}
my $dbh = Golem::get_db();
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE ip = ? and mask = ?");
$sth->execute($ip, $mask);
my $net = $sth->fetchrow_hashref();
$sth->finish();
if ($net->{'id'}) {
return Golem::get_net_by_id($net->{'id'}, $opts);
}
else {
return 0;
}
}
sub get_net_by_id {
my ($id, $opts) = @_;
Golem::die("Programmer error: get_net_by_id expects network id")
unless $id;
$opts = {} unless $opts;
my $dbh = Golem::get_db();
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE id = ?");
$sth->execute($id);
my $r = $sth->fetchrow_hashref();
if ($r->{'id'}) {
$r->{'ip_str'} = Golem::ipv4_int2str($r->{'ip'});
$r->{'net_with_mask'} = $r->{'ip_str'} . "/" . $r->{'mask'};
if ($r->{'name'} =~ /VLAN([\d]+)/o) {
$r->{'vlan'} = $1;
}
else {
$r->{'vlan'} = "";
}
if ($opts->{'with_props'} || $opts->{'with_all'}) {
$r = Golem::load_props("net_v4", $r);
}
return $r;
}
else {
return 0;
}
}
sub insert_net {
my ($net) = @_;
Golem::die("Programmer error: insert_net expects net object")
unless $net && ($net->{'ip_str'} || $net->{'ip'}) &&
$net->{'mask'} && $net->{'name'};
if ($net->{'ip_str'}) {
$net->{'ip'} = Golem::ipv4_str2int($net->{'ip_str'});
}
my $enet = Golem::get_net($net->{'ip'}, $net->{'mask'});
return Golem::err("net already exists [$enet->{'ip_str'}/$enet->{'$mask'} ($enet->{'id'})]")
if $enet;
my $dbh = Golem::__insert("net_v4", $net);
return Golem::err($dbh->errstr, $net)
if $dbh->err;
$net->{'id'} = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
return Golem::get_net_by_id($net->{'id'});
}
sub save_net {
my ($net) = @_;
Golem::die("Programmer error: save_net expects net object")
unless $net && $net->{'id'} &&
($net->{'ip_str'} || $net->{'ip'}) &&
$net->{'mask'} && $net->{'name'};
if ($net->{'ip_str'}) {
$net->{'ip'} = Golem::ipv4_str2int($net->{'ip_str'});
}
my $enet = Golem::get_net($net->{'ip'}, $net->{'mask'}, {"with_all" => 1});
if ($enet) {
my $dbh = Golem::__update("net_v4", $net);
return Golem::err($dbh->errstr, $net)
if $dbh->err;
$net = Golem::save_props("net_v4", $net);
}
else {
return Golem::insert_net($net);
}
return $net;
}
sub delete_net {
my ($net) = @_;
Golem::die("Programmer error: delete_net expects net object")
unless $net && $net->{'id'};
my $dbh = Golem::get_db();
Golem::unset_row_tag("net_v4", $net->{'id'});
$dbh->do("DELETE from net_v4prop where net_v4id = ?", undef, $net->{'id'});
$dbh->do("DELETE from net_v4propblob where net_v4id = ?", undef, $net->{'id'});
$dbh->do("DELETE FROM net_v4 WHERE net_v4.id = ?", undef, $net->{'id'});
return Golem::err($dbh->errstr, $net)
if $dbh->err;
return {};
}
sub get_containing_net {
my ($ip, $opts) = @_;
Golem::die("Programmer error: get_containing_net expects ip address")
unless $ip;
Golem::trim(\$ip);
if ($ip =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/o) {
$ip = Golem::ipv4_str2int($1);
}
my $dbh = Golem::get_db();
# choose nearest (by ip desc) and smallest (by mask desc) known network
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE ip < ? and mask <> 32
order by ip desc, mask desc");
$sth->execute($ip);
my $net;
while(my $r = $sth->fetchrow_hashref()) {
# choose first network that includes tested ip address if any
if ($r->{'ip'} + ipv4_mask2offset($r->{'mask'}) ge $ip) {
return Golem::get_net_by_id($r->{'id'}, $opts);
}
}
return 0;
}
sub get_net_by_vlan {
my ($vlan, $opts) = @_;
Golem::die("Programmer error: get_net_by_vlan expects vlan name")
unless $vlan;
$vlan =~ s/vlan//go;
$vlan =~ s/\s//go;
my $dbh = Golem::get_db();
my $sth = $dbh->prepare("SELECT * FROM net_v4 WHERE mask <> 32 and name like ? order by name");
$sth->execute("VLAN${vlan}%");
while (my $r = $sth->fetchrow_hashref()) {
if ($r->{'name'} =~ /VLAN${vlan}\s/o) {
return Golem::get_net($r->{'ip'}, $r->{'mask'}, $opts);
}
}
return 0;
}
sub is_ipv4 {
my ($str) = @_;
return $str =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/o;
}
1;