2011-09-17 26 views
5

Estoy trabajando en un programa de espacio de usuario Linux que recibe paquetes de publicidad de enrutador IPv6. Como parte de RFC4861, necesito verificar la suma de comprobación ICMPv6. Basado en mi investigación, la mayoría de los cuales se refiere a la suma de comprobación de IP en general si calcula los complementos de suma de comprobación del pseudo encabezado de IPv6 y el contenido del paquete el resultado debe ser 0xffff. Pero sigo obteniendo una suma de comprobación de 0x3fff.¿Cómo valido la suma de comprobación ICMPv6? (¿Por qué sigo recibiendo una suma de comprobación de 0x3fff?)

¿Hay algún problema con la implementación de mi suma de comprobación? ¿el kernel de Linux verifica la suma de comprobación de ICMPv6 antes de pasar los paquetes al espacio de usuario? ¿Existe una buena fuente de referencia para los buenos paquetes ICMPv6 conocidos para probar?

uint16_t 
checksum(const struct in6_addr *src, const struct in6_addr *dst, const void *data, size_t len) { 
    uint32_t checksum = 0; 
    union { 
     uint32_t dword; 
     uint16_t word[2]; 
     uint8_t byte[4]; 
    } temp; 

    // IPv6 Pseudo header source address, destination address, length, zeros, next header 
    checksum += src->s6_addr16[0]; 
    checksum += src->s6_addr16[1]; 
    checksum += src->s6_addr16[2]; 
    checksum += src->s6_addr16[3]; 
    checksum += src->s6_addr16[4]; 
    checksum += src->s6_addr16[5]; 
    checksum += src->s6_addr16[6]; 
    checksum += src->s6_addr16[7]; 

    checksum += dst->s6_addr16[0]; 
    checksum += dst->s6_addr16[1]; 
    checksum += dst->s6_addr16[2]; 
    checksum += dst->s6_addr16[3]; 
    checksum += dst->s6_addr16[4]; 
    checksum += dst->s6_addr16[5]; 
    checksum += dst->s6_addr16[6]; 
    checksum += dst->s6_addr16[7]; 

    temp.dword = htonl(len); 
    checksum += temp.word[0]; 
    checksum += temp.word[1]; 

    temp.byte[0] = 0; 
    temp.byte[1] = 0; 
    temp.byte[2] = 0; 
    temp.byte[3] = 58; // ICMPv6 
    checksum += temp.word[0]; 
    checksum += temp.word[1]; 

    while (len > 1) { 
     checksum += *((const uint16_t *)data); 
     data = (const uint16_t *)data + 1; 
     len -= 2; 
    } 

    if (len > 0) 
     checksum += *((const uint8_t *)data); 

    printf("Checksum %x\n", checksum); 

    while (checksum >> 16 != 0) 
     checksum = (checksum & 0xffff) + (checksum >> 16); 

    checksum = ~checksum; 

    return (uint16_t)checksum; 
} 
+0

¿funciona en una máquina endian grande o pequeña? – Alnitak

+0

Little endian (x86_64), pero desde lo que he leído verificando la suma de comprobación debe ser endian independiente. – dlundquist

+0

+1, simplemente para "Basado en mi investigación" – Flexo

Respuesta

1

El ciclo while es excesivo. El cuerpo solo ocurrirá una vez.

while (checksum >> 16 != 0) 
    checksum = (checksum & 0xffff) + (checksum >> 16); 

checksum = ~checksum; 

return (uint16_t)checksum; 

lugar

checksum += checksum >> 16; 

return (uint16_t)~checksum; 

esto es innecesario. len siempre es 16 bits

temp.dword = htonl(len); 
checksum += temp.word[0]; 
checksum += temp.word[1]; 

Esto no es necesario. La constante es siempre 00 00 00 58, por lo que sólo tiene que añadir 58.

temp.byte[0] = 0; 
temp.byte[1] = 0; 
temp.byte[2] = 0; 
temp.byte[3] = 58; // ICMPv6 
checksum += temp.word[0]; 
checksum += temp.word[1]; 

Su algoritmo busca generalmente la derecha de otro modo, a excepción de la forma en que maneja el orden de bits de los números enteros y el último byte byte impar. A partir de cómo leo el protocolo, los bytes se deben sumar en orden big-endian, es decir, los bytes 0xAB 0xCD se deben interpretar como el 0xABCD de 16 bits. Su código depende del orden de su máquina.

El orden en que se generan los enteros afectará el número de acarreos, que está agregando correctamente en la suma de comprobación. Pero si su código coincide con su máquina de destino, entonces el último byte de número impar es incorrecto. 0xAB daría como resultado 0xAB00, no 0x00AB como está escrito.

+0

Estoy de acuerdo con todas las sugerencias de código, este fue mi cuarto intento e intenté seguir el ejemplo de RFC 1071 4.1 lo más cerca posible. Estaba pensando que el último byte era incorrecto, pero mi caso es que el paquete tiene exactamente 64 bytes de longitud. – dlundquist

1

Si esto se ejecuta en una máquina ascendente hacia la izquierda, entonces creo que necesita (mucho) más el intercambio de bytes, mientras que la acumulación de la suma de comprobación.

Por ejemplo, en una pequeña máquina endian el elemento s6_addr16[0] de una dirección IPv6 típica a partir 2001: contendrá 0x0120, y no 0x2001. Esto colocará sus bits de transporte en el lugar equivocado.

El código de longitud aparece OK ya que está utilizando htonl() allí, pero el 0x00 0x00 0x00 0x58 y la lógica de acumulación de mensajes subsiguientes no. Creo que los bits sobrantes también deberían estar en el byte alto, no en el byte bajo como ocurre en el código.

También, usando 0x0000 para el pseudo encabezado checksum bytes es lo que debe hacer cuando genera la suma de comprobación. Para valide la suma de comprobación use los bytes de suma de comprobación reales recibidos en el RA IPv6, y luego debe obtener 0xffff como valor eventual.

+0

Mi comprensión es que la suma de verificación se completa desde los datos en el pseudo encabezado IPv6, en el que las direcciones están en un red endineness de trabajo y no efectuado por el host. Solo el campo de longitud ya está convertido en endineidad del host. También recibo anuncios de enrutadores de tres enrutadores y este algoritmo de suma de comprobación calcula la misma suma de comprobación para todos los anuncios recibidos. – dlundquist

1

Encontré mi error: tenía un búfer de entrada de 256 bytes y asumí que el elemento iov_len de msg_iov en recvmsg() se modificó para devolver la longitud de los datos recibidos. Como la longitud de los anuncios de mi enrutador era 64 bytes constantes, la diferencia entre estas longitudes dio como resultado un error constante en la suma de comprobación.No necesité cambiar el orden de bytes para verificar la suma de comprobación (aunque no he tenido un paquete ICMPv6 de longitud impar para verificar mi manejo del byte final en el caso de longitud impar.

Además, el NOT final de la suma de comprobación solo es necesario para calcular la suma de comprobación, no para verificarla. Con el código anterior checksum() se devolverá 0 si la suma de comprobación es válida.

Cuestiones relacionadas