Siguiente: Políticas de acceso
Subir: Detalles Implementación
Anterior: Comprobación de datos de
  Índice General
Calidad de servicio
Para empezar con la calidad de servicio se invoca el script inicioQoS desde dentro
del script initialize.fw que es ejecutado al iniciar la pasarela. El script inicioQoS es
el siguiente:
#!/bin/bash
#Creamos la discipllina de cola de subida
tc qdisc del dev $ExternalDevice root
tc qdisc add dev $ExternalDevice root handle 1 htb default 10
#Creamos la disciplina de cola de bajada
tc qdisc del dev $InternalDevice root
tc qdisc add dev $InternalDevice root handle 1 htb default 10
#Creamos la clase raiz de subida
tc class add dev $ExternalDevice parent 1: classid 1:2 htb rate
$MaxBandWidthUp burst 2k
#Creamos la clase por defecto de subida
tc class add dev $ExternalDevice parent 1:2 classid 1:3 htb rate
$MinBandWidthUp ceil $MaxBandWidthUp burst 2k prio 6
tc qdisc add dev $ExternalDevice parent 1:3 handle 3 sfq perturb 10
#Creamos la clase raiz de bajada
tc class add dev $InternalDevice parent 1: classid 1:2 htb rate
$MaxBandWidthDown burst 2k
#Creamos la clase por defecto de bajada
tc class add dev $InternalDevice parent 1:2 classid 1:3 htb rate
$MinBandWidthDown ceil $MaxBandWidthDown burst 2k prio 6
tc qdisc add dev $InternalDevice parent 1:3 handle 3 sfq perturb 10
# Marcamos los paquetes segun el tipo de servicio
iptables -A OUTPUT -t mangle -p tcp --dport 22 -j TOS --set-tos Minimize-Delay
iptables -A OUTPUT -t mangle -p tcp --dport 80 -j TOS
--set-tos Maximize-Throughput
iptables -A OUTPUT -t mangle -p tcp --dport 443 -j TOS
--set-tos Maximize-Throughput
# End
Una vez que han sido iniciadas las clases, la pasarela espera que alguien se conecte.
Cuando alguien intenta conectarse y es redirigido al servidor, es cuando se ejecuta el
script login.cgi, y cuando se crea el mensaje que recibe la pasarela con los parámetros
de la conexión.
Nosotros necesitamos incluir en este mensaje el identificador de cola y los valores de los parámetros
rate, ceil y prio. Para ello hemos utilizado la función consulta_qos,
que devuelve los parámetros del grupo al que pertenece el usuario. Conretamente esta función consulta
la tabla grupos y devuelve una array con los siguientes parámetros: id_cola, rate, ceil y prio.
El id_cola es un parámetro muy delicado, ya que hay que sacarlo de la pila y volver
a dejarlo en ella cuando deje de estar en uso. El script login.cgi se ejecutará una vez cada 45 segundos
por cada usuario, y cada vez que se ejecuta va a llamar a la función consulta_qos. Para
devolver el parámetro id_cola lo primero que hace esta función es mirar si el usuario
está en la tabla conexiones, si está en ella coge el parámetro de esta tabla.
Si el usuario no está conectado es que está haciendo login y hay que asignarle un
identificador nuevo de la pila. En este caso sacamos un identificador de la pila. Cuando
el mensaje sea recibido por la pasarela se invocará el script conexion y automáticamente se
rellenará la tabla conexiones con el id_cola.
Podemos ver en la página
el script login.cgi, y en el primer fragmento comentado está incluido la recogida de parámetros
con la función consulta_qos y su posterior asignación a las variables del cgi para que sean incluidos
en el mensaje.
Cuando la pasarela recibe este mensaje lo desencripta y recibe los parámetros: id_cola, rate, ceil y prio.
Veamos las modificaciones realizadas en el método punch_ticket de la clase Captive.pm, que
es el que se encarga de comprobar la veracidad del mensaje y guardar los valores recibidos:
sub punch_ticket {
my ( $self, $msg, $id ) = @_;
my %auth = $msg->parse;
my $client = $self->{Peer}{$id} ;
if (not $client )
{
# Si no tiene id se termina y hay que devolver el id_cola a la pila del serv.
#
if ($auth{Id_cola})
{
my $id=$auth{Id_cola};
my $servidor=$self->{AuthServiceAddr};
open(FIFO,">/home/nocat/param_fifo");
print FIFO "$servidor libera_id $id";
close FIFO;
$self->log(5,"Liberamos el identificador de cola $id");
}
return $self->log( 2, "Unknown ID notify from $id!" );
}
# Si hay algun error hay que liberar el id_cola
#
if ($client->user and $client->user ne $auth{User})
{
if ($auth{Id_cola})
{
my $id=$auth{Id_cola};
my $servidor=$self->{AuthServiceAddr};
open(FIFO,">/home/nocat/param_fifo");
print FIFO "$servidor libera_id $id";
close FIFO;
$self->log(5,"Liberamos el identificador de cola $id");
}
return $self->log( 2,"Bad user/id match
from $id: $auth{User} != " . $client->user );
}
if ($client->token ne $auth{Token})
{
if ($auth{Id_cola})
{
my $id=$auth{Id_cola};
my $servidor=$self->{AuthServiceAddr};
open(FIFO,">/home/nocat/param_fifo");
print FIFO "$servidor libera_id $id";
close FIFO;
$self->log(5,"Liberamos el identificador de cola $id");
}
return $self->log( 2, "Bad token match
from $id: $auth{Token} != " . $client->token );
}
if (not $self->{IgnoreMAC} and $client->mac ne $auth{Mac})
{
if ($auth{Id_cola})
{
my $id=$auth{Id_cola};
my $servidor=$self->{AuthServiceAddr};
open(FIFO,">/home/nocat/param_fifo");
print FIFO "$servidor libera_id $id";
close FIFO;
$self->log(5,"Liberamos el identificador de cola $id");
}
return $self->log( 2,"Bad MAC match
from $id: $auth{Mac} != " . $client->mac );
}
# Identify the user and class.
$client->user( $auth{User} );
$client->groups( $auth{Member} );
# Asignamos id_cola,ceil y prio
#
$client->id_cola( $auth{Id_cola} );
$client->rate( $auth{Rate} );
$client->ceil( $auth{Ceil} );
$client->prio( $auth{Prio} );
# Store the new token away for when the peer renews its login.
$client->token(1);
# Perform the requested action.
if ( $auth{Action} eq PERMIT ) {
$self->permit( $client );
} elsif ( $auth{Action} eq DENY ) {
$self->deny( $client );
}
$self->log( 9, "Available MACs: @{[ keys %{$self->{Peer}} ]}" );
return \
%auth;
}
En el último fragmento es donde se asignan estos parámetros, como se puede observar
se usan nuevos métodos en la clase peer.pm:
package NoCat::Peer;
.........
sub id_cola {
my ( $self, $id_cola ) = @_;
$self->{Id_cola} = $id_cola if defined $id_cola;
return $self->{Id_cola};
}
sub rate {
my ( $self, $rate ) = @_;
$self->{Rate} = $rate if defined $rate;
return $self->{Rate};
}
sub ceil {
my ( $self, $ceil ) = @_;
$self->{Ceil} = $ceil if defined $ceil;
return $self->{Ceil};
}
sub prio {
my ( $self, $prio ) = @_;
$self->{Prio} = $prio if defined $prio;
return $self->{Prio};
}
Estos métodos, a su vez definen variables de la clase Peer que guardan
un valor para los parámetros id_cola, rate, ceil y prio de cada cliente.
Ya tenemos los parámetros disponibles en la pasarela como unas variables
más del cliente. Para crear la nueva clase que corresponde a un cliente
con sus parámetros usamos el script qos.fw:
#!/bin/sh
#
# Note: your PATH is inherited from the gateway process
#
action=$1
ip=$2
id_cola=$3
rate=$4Kbit
ceil=$5Kbit
prio=$6
if [ -z "$action" -o -z "$ip" -o -z "$id_cola" ]; then
echo
echo Usage: $0 [add\|del] [IP] [Classid] [OPTIONS]
echo add OPTIONS: [Ceil] [Prio]
echo Example: $0 add 10.0.0.105 20 100 5
echo : $0 del 10.1.1.1 20
exit 1
elif [ "$action" = "add" ]; then
if [ -z "$rate" -o -z "$ceil" -o -z "$prio" ]; then
echo
echo Usage: $0 [add\|del] [IP] [Classid] [OPTIONS]
echo add OPTIONS: [Rate] [Ceil] [Prio]
echo Example: $0 add 10.0.0.105 2F 35 100 5
echo : $0 del 10.1.1.1 20
exit 1
fi
fi
#Se crea una clase, se le conecta con una disciplina de colas y un filtro
# que dirige los paquetes del cliente a esa clase.
if [ "$action" = "add" ]; then
tc class add dev $InternalDevice parent 1:2 classid 1:$id_cola htb
rate $rate ceil $ceil burst 2k prio $prio
tc qdisc add dev $InternalDevice parent 1:$id_cola handle $id_cola
sfq perturb 10
tc filter add dev $InternalDevice parent 1:0 protocol ip prio 0x$id_cola
u32 match ip dst $ip classid 1:$id_cola
#Para borrarlo se hace en orden inverso
elif [ "$action" = "del" ]; then
tc filter del dev $InternalDevice parent 1:0 protocol ip prio 0x$id_cola
u32 match ip dst $ip classid 1:$id_cola
tc qdisc del dev $InternalDevice parent 1:$id_cola handle $id_cola
sfq perturb 10
tc class del dev $InternalDevice parent 1:2 classid 1:$id_cola
else
echo "FATAL: Accion incorrecta: $action!"
exit 1
fi
#
# Fin
#
Para invocar el scrip qos.fw se han creado tres métodos nuevos
en la clase Firewall:
package NoCat::Firewall;
......
@REQUIRED = qw( ResetCmd PermitCmd DenyCmd GatewayMode
InsertQoSCmd DeleteQoSCmd);
......
my @Perform_Export = qw(
InternalDevice ExternalDevice LocalNetwork AuthServiceAddr DNSAddr
GatewayAddr GatewayPort IncludePorts ExcludePorts AllowedWebHosts
MembersOnly RouteOnly IgnoreMAC
Policy1 Policy2 Policy3 Policy4 Policy5 Policy6
MarkPolicy1 MarkPolicy2 MarkPolicy3 MarkPolicy4 MarkPolicy5 MarkPolicy6
MaxBandWidthDown MaxBandWidthUp MinBandWidthDown MinBandWidthUp
);
......
sub ins_qos
{
my $self = shift;
$self->actua( InsertQoS => @_ );
}
sub del_qos
{
my $self = shift;
$self->actua( DeleteQoS => @_ );
}
sub actua {
my ( $self, $action, $ip, $id_cola, $rate, $ceil, $prio ) = @_;
my $cmd = $self->format( $self->{"\u${action}Cmd"}, {
Id_cola => $id_cola,
Rate => $rate,
Ceil => $ceil,
Prio => $prio,
IP => $ip,
});
local %ENV = %ENV;
$ENV{$_} =(defined( $self->{$_}) ? $self->{$_} : "" ) for @Perform_Export;
$ENV{PATH} =
"$FindBin::Bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
system $cmd;
}
Para que esto funcione se han definido en el archivo de configuración de la pasarela
las directivas:
# Ancho de banda minimo y maximo por defecto
MinBandWidthDown 35Kbit
MinBandWidthUp 23Kbit
MaxBandWidthDown 140Kbit
MaxBandWidthUp 95Kbit
Los métodos ins_qos y del_qos del firewall se invocan en el momento
que el usuario entra en el sistema y cuando sale del mismo respectivamente:
- El momento de entrar en el sistema es en el método permit4.2
de la clase gateway. Se invoca el método ins_qos
justo detrás de pasar a p_remoto la petición
para invocar el script conexión.
- La salida del sistema se produce en el método deny4.3.
Desde el método deny se llama a del_qos justo despues de cambiar las reglas del firewall.
Recapitulando, ya hemos visto como pasar los parámetros de calidad del servidor,
como crear la nueva clase para ese usuario y como borrarla. Lo que nos falta por ver
es la forma de devolver al servidor el identificador de cola de dicha clase, para
que lo introduzca otra vez en la pila. Para esto se ha creado un pequeño script en el
servidor que llama a la función libera_id del módulo ControlNocat.pm. Lo único que hace
esta función es insertar una nueva fila en la tabla id_cola con el identificador devuelto.
Este script debe ser invocado por la pasarela en varios casos:
- Cuando un usuario abandona el sistema. Independientemente del motivo por el que
abandona el sistema, siempre se llama al método deny (de la clase gateway), si este usuario estaba activo. Dentro del método
deny4.4 se invoca el proceso para liberar el identificador de clase.
- Cuando un usuario ha intentado conectarse pero se produce algún tipo de error. Esto ocurre
cuando un cliente recibe un identificador pero la conexión falla, ya sea por que expire el temporizador,
o porque la MAC sea incorrecta, o el token... En este caso no se llama el metodo deny ya que todavia
no se había permitido el accceso a la red, en cambio el servidor de autenticación si que ha proporcionado
a ese cliente un id_cola que debe ser devuelto a la pila. Estas llamadas se realizan todas desde el método
punch_ticket4.5 de la clase captive.
Para invocar el script libera_id la pasarela vuelve a usar el proceso p_remoto.
Siguiente: Políticas de acceso
Subir: Detalles Implementación
Anterior: Comprobación de datos de
  Índice General
Jesús Martín
2003-09-16