Abills+Cisco сброс пользователя через POD
Добавлено: Пн май 05, 2008 6:51 am
Интеграция с Cisco - сброс пользователя через POD. Не требует использования SNMP или rsh на циске.
Возможно, кому-то ещё это понадобится. Проверялось на Cisco 2821 и Abills 0.37b на Debian Etch. Для сброса пользователя используется аттрибут Framed-Ip-Address. Через Acct-Session-Id фокус не удался, его значецие Abills хранит в виде хэша.
P.S. Спасибо разработчикам и персонально Asmodeus за отличный открытый биллинг.
конфигурация на Cisco:
конфигурация Abills:
cat /usr/abills/Abills/nas.pl | grep -v "#" | grep -v "^$"
Возможно, кому-то ещё это понадобится. Проверялось на Cisco 2821 и Abills 0.37b на Debian Etch. Для сброса пользователя используется аттрибут Framed-Ip-Address. Через Acct-Session-Id фокус не удался, его значецие Abills хранит в виде хэша.
P.S. Спасибо разработчикам и персонально Asmodeus за отличный открытый биллинг.
конфигурация на Cisco:
Код: Выделить всё
!
!c2800nm-adventerprisek9-mz.124-11.XW2.bin
!
aaa new-model
!
!
aaa authentication login default local-case
aaa authentication login sdm_vpn_xauth_ml_1 local
aaa authentication ppp default group radius
aaa authorization exec default local none
aaa authorization network default group radius if-authenticated
aaa authorization network sdm_vpn_group_ml_1 local
aaa accounting delay-start
aaa accounting update periodic 1
aaa accounting network default start-stop group radius
!
aaa server radius dynamic-author
client 192.168.100.242 server-key ********
auth-type any
!
aaa session-id unique
!
!
vpdn enable
!
vpdn-group 1
! Default PPTP VPDN group
accept-dialin
protocol pptp
virtual-template 1
local name Cisco_VPN_PPTP_server
ip mtu adjust
!
!
interface Virtual-Template1
ip unnumbered Vlan60
ip verify unicast reverse-path
no ip unreachables
no ip proxy-arp
ip nat inside
ip virtual-reassembly
autodetect encapsulation ppp
no peer default ip address
compress mppc
ppp mtu adaptive
ppp encrypt mppe 128 required
ppp authentication ms-chap-v2
ppp ipcp dns 192.168.100.1 192.168.100.254
ppp ipcp wins 192.168.100.254
!
interface Vlan60
description ISP1
ip address *.*.*.* 255.255.255.248
ip access-group From-INTERNET in
ip flow ingress
ip flow egress
ip nat outside
ip virtual-reassembly
!
!
radius-server attribute 44 include-in-access-req
radius-server attribute 32 include-in-access-req
radius-server configure-nas
radius-server host 192.168.100.242 auth-port 1812 acct-port 1813
radius-server timeout 30
radius-server key ********
!
cat /usr/abills/Abills/nas.pl | grep -v "#" | grep -v "^$"
Код: Выделить всё
use BER;
use SNMP_Session;
use SNMP_util;
my $PPPCTL = '/usr/sbin/pppctl';
my $RADCLIENT = '/usr/bin/radclient';
my $SUDO = '/usr/local/bin/sudo';
my $NAS;
my $nas_type = '';
my %stats = ();
sub hangup {
my ($Nas, $PORT, $USER, $attr) = @_;
$NAS = $Nas;
$nas_type = $NAS->{NAS_TYPE};
if ($nas_type eq 'exppp') {
hangup_exppp($NAS, $PORT, $attr);
}
elsif ($nas_type eq 'pm25') {
hangup_pm25($NAS, $PORT, $attr);
}
elsif ($nas_type eq 'radpppd') {
hangup_radpppd($NAS, $PORT, $attr);
}
elsif ($nas_type eq 'mikrotik') {
radius_disconnect($NAS, $PORT, $USER);
}
elsif ($nas_type eq 'usr') {
hangup_snmp($NAS, $PORT, { OID => '.1.3.6.1.4.1.429.4.10.13.'. $PORT,
TYPE => 'integer',
VALUE => 9 });
}
[b] elsif ($nas_type eq 'cisco') {
radius_disconnect2($NAS, $PORT, $attr);
}[/b]
elsif ($nas_type eq 'mpd') {
hangup_mpd($NAS, $PORT);
}
elsif ($nas_type eq 'mpd4') {
hangup_mpd4($NAS, $PORT, $attr);
}
elsif ($nas_type eq 'ipcad') {
hangup_ipcad($NAS, $PORT, $USER, $attr);
}
elsif ($nas_type eq 'patton') {
hangup_patton29xx($NAS, $PORT, $attr);
}
elsif ($nas_type eq 'pppd' || $nas_type eq 'lepppd') {
hangup_pppd($NAS, $PORT, $attr);
}
else {
return 1;
}
return 0;
}
sub get_stats {
my ($Nas, $PORT, $attr) = @_;
$NAS = $Nas;
$nas_type = $NAS->{NAS_TYPE};
if ($nas_type eq 'usr') {
%stats = stats_usrns($NAS, $PORT);
}
elsif ($nas_type eq 'patton') {
%stats = stats_patton29xx($NAS, $PORT);
}
elsif ($nas_type eq 'pm25') {
%stats = stats_pm25($NAS, $PORT);
}
elsif ($nas_type eq 'dslmax') {
my $user_ip_address = $attr->{user_ip_address};
%stats = stats_dslmax($NAS, $PORT, $user_ip_address);
}
else {
return undef;
}
return \%stats;
}
sub telnet_cmd {
my($hostname, $commands, $attr)=@_;
my $port = 23;
if ($hostname =~ /:/) {
($hostname, $port)=split(/:/, $hostname, 2);
}
my $timeout = defined($attr->{'TimeOut'}) ? $attr->{'TimeOut'} : 5;
use Socket;
my $dest = sockaddr_in($port, inet_aton("$hostname"));
if(! socket(SH, PF_INET, SOCK_STREAM, getprotobyname('tcp'))) {
print "ERR: Can't init '$hostname:$port' $!";
return 0;
}
if(! CORE::connect(SH, $dest) ) {
print "ERR: Can't connect to '$hostname:$port' $!";
return 0;
}
log_print('LOG_DEBUG', "Connected to $hostname:$port");
my $sock = \*SH;
my $MAXBUF= 512;
my $input = '';
my $len = 0;
my $text = '';
my $inbuf = '';
my $res = '';
my $old_fh = select($SH); $| = 1; select($old_fh);
SH->autoflush(1);
foreach my $line (@$commands) {
my ($waitfor, $sendtext)=split(/\t/, $line, 2);
$input = '';
if ($waitfor eq '-') {
send($sock, "$sendtext\n", 0, $dest) or die log_print('LOG_INFO', "Can't send: '$text' $!");
}
do {
recv($sock, $inbuf, $MAXBUF, 0);
$input .= $inbuf;
$len = length($inbuf);
alarm 5;
} while ($len >= $MAXBUF || $len < 4);
log_print('LOG_DEBUG', "Get: \"$input\"\nLength: $len");
log_print('LOG_DEBUG', " Wait for: '$waitfor'");
$text = $sendtext;
log_print('LOG_DEBUG', "Send: $text");
send($sock, "$text\n", 0, $dest) or die log_print('LOG_INFO', "Can't send: '$text' $!");
};
$res .= "$input\n";
}
close(SH);
return $res;
}
sub telnet_cmd2 {
my($host, $commands, $attr)=@_;
my $port = 23;
if ($host =~ /:/) {
($host, $port)=split(/:/, $host, 2);
}
use IO::Socket;
use IO::Select;
my $data;
my $res;
my $timeout = defined($attr->{'TimeOut'}) ? $attr->{'TimeOut'} : 5;
my $socket = new IO::Socket::INET(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
TimeOut => $timeout
) or log_print('LOG_DEBUG', "ERR: Can't connect to '$host:$port' $!");
log_print('LOG_DEBUG', "Connected to $host:$port");
foreach my $line (@$commands) {
my ($waitfor, $sendtext)=split(/\t/, $line, 2);
$socket->send("$sendtext");
while(<$socket>) {
$res .= $_;
}
}
close($socket);
return $res;
}
sub stats_pm25 {
my ($NAS, $PORT) = @_;
my %stats = (in => 0,
out => 0);
my $PM25_PORT=$PORT+2;
my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';
my ($in) = snmpget($SNMP_COM .'@'. $NAS->{NAS_IP}, ".1.3.6.1.2.1.2.2.1.10.$PM25_PORT");
my ($out) = snmpget($SNMP_COM .'@'.$NAS->{NAS_IP}, ".1.3.6.1.2.1.2.2.1.16.$PM25_PORT");
if (! defined($in)) {
$stats{error}=1;
}
elsif (int($in) + int($out) > 0) {
$stats{in} = int($in);
$stats{out} = int($out);
}
return %stats;
}
sub hangup_pm25 {
my ($NAS_IP, $PORT) = @_;
push @commands, "login:\t$NAS->{NAS_MNG_USER}";
push @commands, "Password:\t$NAS->{NAS_MNG_PASSWORD}";
push @commands, ">\treset S$PORT";
push @commands, ">exit";
my $result = telnet_cmd("$NAS->{NAS_IP}", \@commands);
print $result;
return 0;
}
sub stats_usrns {
my ($NAS, $PORT) = @_;
my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';
my $in = `a=\`$SNMPWALK -v 1 -c "$SNMP_COM" $NAS->{NAS_IP} interfaces.ifTable.ifEntry.ifInOctets.$PORT | awk '{print \$4}'\`; b=\`cat /usr/abills/var/devices/$NAS->{NAS_IP}-$PORT.In\`; c=\`expr \$a - \$b + 0\`; echo \$c`;
my $out = `a=\`$SNMPWALK -v 1 -c "$SNMP_COM" $NAS->{NAS_IP} interfaces.ifTable.ifEntry.ifOutOctets.$PORT | awk '{print \$4}'\`; b=\`cat /usr/abills/var/devices/$NAS->{NAS_IP}-$PORT.Out\`; c=\`expr \$a - \$b + 0\`; echo \$c`;
$stats{in} = int($in);
$stats{out} = int($out);
return %stats;
}
sub stats_ppp {
my ($NAS_IP, $PORT) = @_;
use IO::Socket;
my $port = 30006;
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
$port = $mng_port || 0;
my $remote = IO::Socket::INET -> new(Proto => "tcp",
PeerAddr => "$NAS",
PeerPort => "$port")
or print "cannot connect to pppcons port at $NAS->{NAS_IP}:$port $!\n";
while ( <$remote> ) {
($radport, $in, $out, $tun) = split(/ +/, $_);
$stats{$NAS->{NAS_IP}}{$radport}{in} = $in;
$stats{$NAS->{NAS_IP}}{$radport}{out} = $out;
$stats{$NAS->{NAS_IP}}{$radport}{tun} = $tun;
}
}
sub stats_dslmax {
my ($NAS, $PORT, $IP) = @_;
my %stats = ();
my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';
my $output = `id=\`$SNMPWALK -c "$SNMP_COM" -v1 $NAS->{NAS_IP} RFC1213-MIB::ipRouteIfIndex.$IP | awk '{ print \$4 }'\`; a=\`$SNMPWALK -c "$SNMP_COM" -v1 $NAS IF-MIB::ifInOctets.\$id | awk '{print \$4}'\`; b=\`$SNMPWALK -c "$SNMP_COM" -v1 $NAS IF-MIB::ifOutOctets.\$id | awk '{print \$4}'\`; echo \$a \$b`;
my ($in, $out)=split(/ /, $out);
$stats{in} = $in;
$stats{out} = $out;
return %stats;
}
sub hangup_snmp {
my ($NAS, $PORT, $attr) = @_;
my $oid = $attr->{OID};
my $type = $attr->{TYPE} || 'integer';
my $value = $attr->{VALUE};
log_print('LOG_DEBUG', "SNMPSET: $NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP} $oid $type $value");
my $result = snmpset("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", "$oid", "$type", $value);
if ($SNMP_Session::errmsg) {
log_print('LOG_ERR', "$SNMP_Session::errmrnings / $SNMP_Session::errmsg");
}
return $result;
}
sub radius_disconnect {
my ($NAS, $PORT, $USER) = @_;
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
log_print('LOG_DEBUG', " HANGUP: echo \"User-Name=$USER\" | $RADCLIENT $ip:$mng_port 40 '$NAS->{NAS_MNG_PASSWORD}'\n");
my $result = `echo "User-Name=$USER" | $RADCLIENT $ip:$mng_port 40 '$NAS->{NAS_MNG_PASSWORD}'`;
return $result;
}
sub hangup_mikrotik_telnet {
my ($NAS_IP, $PORT, $USER) = @_;
push @commands, "Login:\t$NAS->{NAS_MNG_USER}";
push @commands, "Password:\t$NAS->{NAS_MNG_PASSWORD}";
push @commands, ">/interface pptp-server remove [find user=$USER]";
push @commands, ">quit";
my $result = telnet_cmd_new("$NAS->{NAS_IP}", \@commands);
print $result;
}
sub hangup_ipcad {
my ($NAS_IP, $PORT, $USER_NAME, $attr) = @_;
require Ipn;
Ipn->import();
my $Ipn = Ipn->new($db, \%conf);
$Ipn->acct_stop({ %$attr, SESSION_ID => $attr->{ACCT_SESION_ID} });
my $cmd = $conf{IPN_FW_STOP_RULE};
my $ip = $attr->{FRAMED_IP_ADDRESS};
my @ip_array = split(/\./, $ip, 4);
my $rule_num = $conf{IPN_FW_FIRST_RULE} + $ip_array[3];
$cmd =~ s/\%IP/$ip/g;
$cmd =~ s/\%NUM/$rule_num/g;
$cmd =~ s/\%LOGIN/$USER_NAME/g;
log_print('LOG_DEBUG', "$cmd");
if ($attr->{debug} && $attr->{debug} > 4) {
print $cmd."\n";
}
print $cmd."\n";
my $result = system($cmd);
print $result;
}
sub radius_disconnect2 {
my ($NAS, $PORT, $attr, $accid) = @_;
my $accid = $attr->{FRAMED_IP_ADDRESS};
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
log_print('LOG_DEBUG', " HANGUP: echo \"Framed-Ip-Address = $accid\" | $RADCLIENT $ip:$mng_port 40 '$NAS->{NAS_MNG_PASSWORD}'\n");
my $result = `echo "Framed-Ip-Address = $accid" | $RADCLIENT $ip:$mng_port 40 '$NAS->{NAS_MNG_PASSWORD}'`;
return $result;
}
sub hangup_cisco {
my ($NAS, $PORT, $attr) = @_;
my $exec;
my $command = '';
my $user = $attr->{USER};
if ($NAS->{NAS_MNG_USER}) {
my $cisco_user=$NAS->{NAS_MNG_USER};
$command = "/usr/bin/rsh -l $cisco_user $NAS->{NAS_IP} show users | grep -i \" \$1 \" | awk '{print \$1}';";
log_print('LOG_DEBUG', "$command");
my $VIRTUALINT=`$command`;
$command = "echo $VIRTUALINT echo | sed -e \"s/[[:alpha:]]*\\([[:digit:]]\\{1,\\}\\)/\\1/\"";
log_print('LOG_DEBUG', "$command");
$PORT=`$command`;
$command = "/usr/bin/rsh -4 -n -l $cisco_user $NAS->{NAS_IP} clear interface Virtual-Access $PORT";
log_print('LOG_DEBUG', "$command");
$exec = `$command`;
}
else {
my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';
$command = "/usr/bin/which snmpset";
log_print('LOG_DEBUG', "$command");
my $SNMPSET=`$command`;
$SNMPSET =~ s/\n//;
$command = "finger \@$NAS->{NAS_IP} | awk '{print \$1 \" \" \$2}' | grep $user\"\$\" | awk '{print \$1}' | sed s/Vi/Virtual-Access/g";
log_print('LOG_DEBUG', "$command");
my $INTNAME=`$command`;
$INTNAME =~ s/\n//;
$command = "$SNMPWALK -v 1 -c \"$SNMP_COM\" -O n $NAS->{NAS_IP} .1.3.6.1.2.1.2.2.1.2 | grep $INTNAME\"\$\" | awk '{print \$1}' | sed s/.1.3.6.1.2.1.2.2.1.2.//g";
log_print('LOG_DEBUG', "$command");
my $INTNUM=`$command`;
$INTNUM =~ s/\n//;
$command = "$SNMPSET -v 1 -c \"$SNMP_COM\" $NAS->{NAS_IP} 1.3.6.1.2.1.2.2.1.7.$INTNUM i 2 > /dev/null 2>&1";
log_print('LOG_DEBUG', "$command");
$exec=`$command`;
}
return $exec;
}
sub hangup_dslmax {
my ($NAS_IP, $PORT) = @_;
my @commands = ();
push @commands, "word:\t$NAS->{NAS_MNG_PASSWORD}";
push @commands, ">\treset S$NAS->{PORT}";
push @commands, ">exit";
my $result = telnet_cmd("$NAS->{NAS_IP}", \@commands);
print $result;
return 0;
}
sub hangup_exppp {
my ($NAS, $PORT) = @_;
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
my $ctl_port = $mng_port + $PORT;
my $out=`$PPPCTL -p "$NAS->{NAS_MNG_PASSWORD}" $NAS->{NAS_IP}:$ctl_port down`;
return 0;
}
sub stats_exppp {
my ($NAS, $PORT) = @_;
my %stats = ();
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
my $ctlport = $mng_port + $PORT;
my $std_out = `$PPPCTL -p "$NAS->{NAS_MNG_PASSWORD}" $NAS->{NAS_IP}:$ctlport ! echo OCTETSIN OCTETSOUT USER`;
my ($in, $out, $user) = split(/ +/, $out);
$stats{in} = $in;
$stats{out} = $out;
return %stats;
}
sub hangup_mpd4 {
my ($NAS, $PORT, $attr) = @_;
my $ctl_port = "pptp$PORT";
if ($attr->{ACCT_SESION_ID}) {
if($attr->{ACCT_SESION_ID} =~ /\d+\-(.+)/) {
$ctl_port = $1;
}
}
my @commands=("\t",
"Username: \t$NAS->{NAS_MNG_USER}",
"Password: \t$NAS->{NAS_MNG_PASSWORD}",
"\] \tbundle $ctl_port",
"\] \tclose",
"\] \texit");
my $result = telnet_cmd("$NAS->{NAS_MNG_IP_PORT}", \@commands);
return 0;
}
sub hangup_mpd {
my ($NAS_IP, $PORT) = @_;
my $ctl_port = "pptp$PORT";
my @commands=("\]\tlink $ctl_port",
"\]\tlink $ctl_port",
"\]\tclose",
"\]\texit");
my $result = telnet_cmd("$NAS->{NAS_MNG_IP_PORT}", \@commands);
print $result;
return 0;
}
sub stats_mpd {
my ($NAS, $PORT) = @_;
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
my $ctlport = $mng_port + $PORT;
my $std_out = `$PPPCTL -p "$NAS->{NAS_MNG_PASSWORD}" $NAS->{NAS_IP}:$ctlport ! echo OCTETSIN OCTETSOUT USER`;
my ($in, $out, $user) = split(/ +/, $out);
$stats{in} = $in;
$stats{out} = $out;
}
sub log_print2 {
my ($type, $text) = @_;
print "$type - $text\n";
}
sub hangup_radpppd {
my ($NAS_IP, $PORT) = @_;
my $RUN_DIR='/var/run';
my $AWK='/bin/awk';
my $CAT='/bin/cat';
my $GREP='/bin/grep';
my $ROUTE='/sbin/route';
my $KILL='/bin/kill';
my $PID_FILE="$RUN_DIR/PPP$PORT.pid";
my $PPP_PID=`$CAT $PID_FILE`;
my $a = `$KILL -1 $PPP_PID`;
return 0;
}
sub stats_pppd {
my ($NAS, $PORT, $IP) = @_;
my $firstnumber = 1000;
my $step = 10;
my $innum = $firstnumber + $PORT * $step;
my $outnum = $firstnumber + $PORT * $step + 5;
$stats{$NAS->{NAS_IP}}{$PORT}{in} = 0;
$stats{$NAS->{NAS_IP}}{$PORT}{out} = 0;
open(FW, "/usr/sbin/ipfw $innum $outnum") || die "Can't open '/usr/sbin/ipfw' $!\n";
while(<FW>) {
($num, $bbyte, $bytes, $trash)=split(/ +/, $_, 4);
if($innum == $num) {
$stats{$SERVER}{$PORT}{in} = $bytes;
}
elsif($outnum == $num) {
$stats{$SERVER}{$PORT}{in} = $bytes;
}
}
close(FW);
}
sub hangup_pppd {
my ($NAS, $id, $attr) = @_;
my $IP = $attr->{FRAMED_IP_ADDRESS} ;
my $result = '';
if ($NAS->{NAS_MNG_IP_PORT} =~ /:/) {
my ($ip, $mng_port)=split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
use IO::Socket;
my $remote = IO::Socket::INET -> new(Proto => "tcp",
PeerAddr => "$ip",
PeerPort => $mng_port
)
or die "cannot connect to rmstats port at $ip:$mng_port $!\n";
print $remote "$IP\n";
$result = <$remote> ;
}
else {
$result = system ("/usr/bin/sudo /usr/abills/misc/pppd_kill $IP");
}
return $result;
}
sub hangup_patton29xx {
my ($NAS, $PORT, $attr) = @_;
my $exec = '';
my %active = ();
my @arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.3");
foreach my $line (@arr) {
if ($line =~ /(\d+):6/) {
$active{$1}=1;
}
}
@arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.9");
foreach my $line (@arr) {
if ($line =~ /(\d+):(\d+)/) {
if ($2 == $PORT && $active{$1} ) {
$exec = snmpset("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.3.$1", 'integer', 10);
last;
}
}
}
return $exec;
}
sub stats_patton29xx {
my ($NAS, $PORT) = @_;
my %stats = (in => 0,
out => 0);
my %active = ();
my @arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.3");
foreach my $line (@arr) {
if ($line =~ /(\d+):6/) {
$active{$1}=1;
}
}
@arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.9");
foreach my $line (@arr) {
if ($line =~ /(\d+):(\d+)/) {
if ($2 == $PORT && $active{$1} ) {
$stats{out} = snmpget("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.36.$1");
$stats{in} = snmpget("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.37.$1");
log_print('LOG_DEBUG', "IFACE: $line INDEX $1 IN: $stats{in} OUT: $stats{out}");
last;
}
}
}
return %stats;
}
1