2010-07-09 22 views
8

Por lo tanto, estoy buscando diferentes funciones hash para utilizar para hash 4 ip y un puerto de tupla para identificar los flujos.función hash para src dest ip + puerto

Uno me encontré fue

((size_t)(key.src.s_addr) * 59)^
((size_t)(key.dst.s_addr))^
((size_t)(key.sport) << 16)^
((size_t)(key.dport))^
((size_t)(key.proto)); 

Ahora para la vida de mí, no puedo explicar el primer usada (59). ¿por qué no 31, y luego por qué te equivocas al multiplicar el deporte por una potencia de 2. ¿Hay una mejor función hash que se use para las direcciones IP?

Respuesta

11

El número primo se usa porque cuando un valor se multiplica por un número primo, tiende a tener una mayor probabilidad de permanecer único cuando se acumulan otras operaciones similares encima de él. El valor específico 59 puede haber sido elegido arbitrariamente o puede ser intencional. Es dificil de decir. Es posible que 59 tendieran a generar una mejor distribución de valores en función de las entradas más probables.

El desplazamiento por 16 puede deberse a que los puertos están limitados al rango 2^16. La función parece mover el puerto de origen a la parte superior del campo de bits mientras deja el puerto de destino en la parte inferior. Creo que esto se puede explicar más en mi próximo párrafo.

Otra razón por la que se lleva a cabo la multiplicación (y esto también es cierto para la operación de cambio) es porque rompe la naturaleza asociativa de la función hash. Recuerde, XOR es asociativo, por lo que las IP src = 192.168.1.1 y dst = 192.168.1.254 harían hash al mismo valor que src = 192.168.1.254 y dst = 192.168.1.1 (intercambiadas) si la multiplicación no estuviera allí.

+0

Entonces, ¿por qué no multiplicar cada ip por la prima en lugar de solo la fuente? – creatiwit

+1

La función hash volvería a asociarse porque '(a * P)^(b * P) = (b * P)^(a * P)'. Aunque es común ver dos números coprime trabajando en tándem para hacer algo similar. –

3

Examine la salida de la función para una distribución uniforme. Si no te gusta, conecta algunos números primos diferentes hasta que obtengas la distribución que te gusta. Hashing puede ser un arte muy oscuro sin una respuesta "correcta".

+0

Sí. Lo más probable es que esta función en particular resulte de probar muchas funciones diferentes en los datos de prueba y ver qué dio el mejor comportamiento. –

2

Personalmente, creo que sería mejor leer los cuatro bytes IP como un largo sin signo que le daría un número aproximadamente en el rango 0 - 2^32-1. Luego calcula cuántos flujos desea tener activos en un momento dado y ese sería su tamaño de tabla de índice.

Tome 2000 por ejemplo. Eso significa que desea mapear 2^32 números en aproximadamente 2^11 indeces (para fluir información). Eso no funcionará porque hashing casi nunca funciona si está lleno al 100% e incluso el 90% puede ser difícil. Usar una tabla de índice que solo llene al 50% (4000 indeces) o incluso al 25% (8000) no es gran cosa con los recuerdos de hoy.

El tamaño exacto de la tabla de índice debe ser un número impar de ubicaciones y preferiblemente un número primo. Esto se debe a que lo más probable es que necesite tener algún control de desbordamiento para manejar las colisiones (dos o más números de IP que después del hash apuntan a la misma ubicación en la tabla de índice), lo que obtendrá. El manejo de desbordamiento debe ser otro número primo menor que el tamaño de la tabla de índice. Todos estos números primos! ¿Qué pasa con ellos de todos modos?

voy a ilustrar con un ejemplo (en C):

idxsiz = prime(2000 * 2); // 50% loading 
ovfjmp = prime(idxsiz/3); 

...

llenar inicialmente la tabla de posiciones idxjmp con un marcado INUSITADO (-1). Tenga una marca SUPRIMIDA lista (-2).

Su número IP entra en el sistema y que busque su historial de flujo (puede o no existir):

stoppos = ip % idxsiz; /* modulo (division) just this once */ 
i = stoppos; 
do 
{ 
    if (index[i] == UNUSED) return NULL; 
    if (index[i] != DELETED) 
    { 
    flowrecptr = &flow_record[index[i]]; 
    if (!flowrecptr->in_use) {/* hash table is broken */} 
    if (flowrecptr->ip == ip) return flowrecptr; 
    } 
    i += ovfjmp; 
    if (i >= idxsiz) i -= idxsiz; 
} 
while (i != stoppos); 
return NULL; 

El INUSITADO sirve como un marcador que este índice no se ha utilizado y que la búsqueda debe parar . El DELETED sirve como marcador de que este índice se ha utilizado pero ya no. Eso significa que la búsqueda debe continuar.

Esto fue cuando intentabas hacer un get. Obtuviste un NULL de get así que necesitas hacer un put que comienzas encontrando la primera posición de índice que contiene UNUSED o BORRADO. Reemplace este valor con un índice a la primera/siguiente fila libre en la tabla flow_record. Marque la fila como in_use. Coloque el número IP original en el miembro ip de la fila flow_record.

Esta es una forma muy simple, pero muy efectiva, de construir un mecanismo hash. Prácticamente todas las optimizaciones en forma de funciones especiales que se utilizarán después de que esta o aquella función hayan fallado mejorarán la eficacia del hash.

El uso de números primos garantizará que, en el peor de los casos donde todas las ubicaciones de índice estén ocupadas, el mecanismo probará cada ubicación individual. Para ilustrar esto: supongamos que idxsiz es uniformemente divisible por ovfjmp: no tendrá mucho manejo de desbordamiento para hablar de ello. 35 y 7 darán como resultado que las ubicaciones 0,7,14,21 y 28 se prueben antes de que el índice salte a 0, donde la prueba de tiempo hará que la búsqueda se detenga.

---------------------- ¡OOPS!

Me perdí que también quería el número de puerto. Suponiendo ip V4 que significa 6 bytes de dirección. Lea esto como un entero sin signo de 64 bits y borre los 16 bits/2 bytes superiores. Luego haces el cálculo del módulo.

2

Brian Gideon prácticamente lo resume; la multiplicación y el cambio están destinados a romper la simetría. Así que esto capta el caso hipotético de Telnetting de máquina A para la máquina B y viceversa y escogen el mismo portnum efímero. No es muy común, pero no imposible. Gran parte de la 5-tupla es bastante constante: el protocolo proviene de un dominio muy pequeño, y también lo hace la mitad de {address, portnum}.

Asumiendo una tabla hash de primer nivel, la constante mágica 59 podría ser reemplazada por cualquier primo, IMO. El (puerto < < 16) también podría ser reemplazado por otro cambio, siempre que no caiga ningún bit o incluso por un término (puerto * some_other_prime).

Para una tabla hash de potencia de dos, todos (menos uno) miembros de la 5-tupla se deben multiplicar por una prima (diferente). (en los viejos tiempos, cuando la división era costosa, hubiera sido una opción)

Cuestiones relacionadas