Escenario
Red diversa conectada al router que da salida a Internet. Usuario demasiado aficionado a las redes de pares al que por desgracia no se le puede cortar sin más la conexión. Tampoco es posible enrutar todo el tráfico a través de un equipo (lo que haría posible poner QoS de verdad), pero disponemos de un equipo Linux permanentemente encendido.
Apaño (aquí hubiera preferido poner "solución", pero me temo que esto se aproxima más a la verdad)
Script en perl que activa o desactiva un filtro del 3com según el número de conexiones que tenga el usuario. Usaremos el directorio $HOME/filtro. Al script lo he llamado conexiones.pl
#!/usr/bin/perl
#
#
use strict;
use HTTP::Request::Common;
use LWP::UserAgent;
my $ruta="$ENV{HOME}/filtro";
sub main {
my @num;
#my @ip;
my $url="http://usuario:clave\@ip_del_router/US/Internet/list_nataddr";
my $pagina;
my $i=0;
my $k;
my @linea;
do {
$pagina=&mira_pagina($url);
$i++;
} while (!$pagina && $i<3);
if ($pagina) {
@linea=split(/\n/,$pagina);
#print "Líneas: $#linea\n";
$i=0;
#print $pagina;
do {
$k=$#num+1 if ($linea[$i] =~ /a\.b\.c\.d/);
push (@num,$1)
if ($linea[$i] =~ /\t \t (\d+)\t\t <\/TD>/);
$i++;
} while ($i<$#linea);
#print "Conexiones totales: ".($num[$k]+$num[$k+1])."\n";
if ($num[$k]+$num[$k+1] > 30) {
unless (&filtrado) {
print "$num[$k]/$num[$k+1] conexiones, activando filtro\n";
system("$ruta/filtroon");
&filtrado(1);
}
}
elsif (&filtrado) {
print "$num[$k]/$num[$k+1] conexiones, desactivando filtro\n";
system("$ruta/filtrooff");
&filtrado(2);
}
}
else {
die ("No puedo descargar $url tras $i intentos\n");
}
}
sub mira_pagina {
my $url=shift;
my $ua = new LWP::UserAgent;
my $res;
$res = $ua->request(GET $url);
if ($res->is_success) {
return $res->content;
}
else {
return;
}
}
sub filtrado {
my $param=shift; # nada=estado, 1 filtrar, 2 quitar flitro
my $estado;
if ($param) {
open (FILTRO,">$ruta/filtrado") || die("No puedo abrir $ruta/filtrado\n");
if ($param==1) {
print FILTRO 1;
}
elsif ($param==2) {
print FILTRO 0;
}
close (FILTRO);
}
else {
open (FILTRO,">$ruta/filtrado") || return 0;
read (FILTRO,$estado,1);
close (FILTRO);
return $estado;
}
}
&main;
A cambiar:
- usuario: un usuario del router con permiso para usar el interfaz web.
- clave: la de usuario en el router.
- ip_del_router: la del 3com en la red local.
- a\.b\.c\.d: IP a vigilar.
- 30: número de conexiones (UDP+TCP) con las que se activará el filtro.
Scripts para activar y desactivar el filtro
Mi intención original era entrar al CLI con expect, pero no he conseguido encontrar en el puñetero manual de 3com la forma de habilitar y deshabilitar filtros desde el CLI (pasarlos de enabled: no a enabled: yes). Si alguien tiene más información, se agradecería que la compartiese. Así que al final he optado por usar el interfaz web con curl.
Nótese que estos scripts cambian el estado del filtro "nombre". Este filtro debe estar creado previamente, y en mi caso contiene la sencilla (y efectiva) regla "Discard Packet if IP Source Address is Equal to a.b.c.d".
- filtroon
#!/bin/sh
curl http://usuario:clave@ip_del_router/US/1/Forms/filterModify \
-d 'usrDefinedFilterName=nombre&usrDefinedFilterEnabled=on&Submit=Modify'
- filtrooff
#!/bin/sh
curl http://usuario:clave@ip_del_router/US/1/Forms/filterModify \
-d 'usrDefinedFilterName=nombre&Submit=Modify'
Ojo que es posible que la url varíe de router a router, pero para averiguarlo basta con habilitar el filtro desde un navegador.
Ya sólo queda poner el script en cron:
*/5 * * * * /usr/bin/perl /ruta_a_home/filtro/conexiones.pl
También es conveniente quitar el filtro de vez en cuando, por si fallase el router (sorpresa) y no se grabase correctamente el estado, se apagase el equipo o cualquier otra eventualidad:
3 */8 * * * rm -f /ruta_a_home/filtro/filtrado ; /ruta_a_home/filtro/filtrooff
No es que sea una solución óptima, pero es más cómodo que poner el filtro a mano cada vez que tengo un ping de 5 segundos.