Foros del Web » Programación para mayores de 30 ;) » Bases de Datos General » Mysql »

IPV6 y IPV4 en campo INT

Estas en el tema de IPV6 y IPV4 en campo INT en el foro de Mysql en Foros del Web. Buenas, He estado buscando informacion sobre como almacenar las IPs en la base de datos. En los textos que he encontrado hay quien dice de ...
  #1 (permalink)  
Antiguo 03/06/2011, 10:27
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
IPV6 y IPV4 en campo INT

Buenas,

He estado buscando informacion sobre como almacenar las IPs en la base de datos. En los textos que he encontrado hay quien dice de almacenar el campo tanto IP4 (165.14.165.45) como IP6 (FEBC:A574:382B:23C1:AA49:4592:4EFE:9982), como cadena, pero claro eso es ineficiente para la base de datos por que un varchar ocupa mucho mas.

Para ello existe ip2long que pasa una ip4 a un int, pero al parecer tiene problemas con ip6. He estado buscando y he encontrado este par de funciones que pasa tanto ip4 y 6 a un int.

Código PHP:
Ver original
  1. function ip2bin($ip){
  2.     if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false)
  3.         return base_convert(ip2long($ip),10,2);
  4.     if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
  5.         return false;
  6.     if(($ip_n = inet_pton($ip)) === false) return false;
  7.     $bits = 15; // 16 x 8 bit = 128bit (ipv6)
  8.     while ($bits >= 0){
  9.         $bin = sprintf("%08b",(ord($ip_n[$bits])));
  10.         $ipbin = $bin.$ipbin;
  11.         $bits--;
  12.     }
  13.     return $ipbin;
  14. }
  15.  
  16. function bin2ip($bin){
  17.    if(strlen($bin) <= 32) // 32bits (ipv4)
  18.        return long2ip(base_convert($bin,2,10));
  19.    if(strlen($bin) != 128)
  20.        return false;
  21.    $pad = 128 - strlen($bin);
  22.    for ($i = 1; $i <= $pad; $i++)
  23.    {
  24.        $bin = "0".$bin;
  25.    }
  26.    $bits = 0;
  27.    while ($bits <= 7)
  28.    {
  29.        $bin_part = substr($bin,($bits*16),16);
  30.        $ipv6 .= dechex(bindec($bin_part)).":";
  31.        $bits++;
  32.    }
  33.    return inet_ntop(inet_pton(substr($ipv6,0,-1)));
  34. }


El resultado generado por ejemplo para la IP6 es algo como el siguiente:
11111110101111001010010101110100001110000010101100 10001111000001101010100100100101000101100100100100 1110111111101001100110000010

Con 128 digitos....... Ahora la duda del millon: ¿Como guardo 128 digitos? Por que en ningun sitio se explica, hay quien dice que usando 2 campos bigint , y en ese mismo sitio pone que cada campo bigint tiene 64 caracteres de capacidad!! :S :S
Los campos bigint unsigned tienen 20 caracteres de capacidad (18.446.744.073.709.551.615 , http://www.desarrolloweb.com/articulos/1054.php), es decir, necesitarias 7 columnas de bigint donde en cada columna se guarden 20, y en la ultima 8.....

No puede ser esa la forma de hacerse :S

7 columnas para almacenar una IP?? Parece de risa.


¿¿No hay una forma general donde se pueda guardar tanto IP4 y 6 de forma general en un campo int ocupando lo minimo posible??

Última edición por leif_sk8er; 03/06/2011 a las 10:35
  #2 (permalink)  
Antiguo 03/06/2011, 11:12
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: IPV6 y IPV4 en campo INT

Cita:
El resultado generado por ejemplo para la IP6 es algo como el siguiente:
11111110101111001010010101110100001110000010101100 10001111000001101010100100100101000101100100100100 1110111111101001100110000010

Con 128 digitos....... Ahora la duda del millon: ¿Como guardo 128 digitos? Por que en ningun sitio se explica, hay quien dice que usando 2 campos bigint , y en ese mismo sitio pone que cada campo bigint tiene 64 caracteres de capacidad!! :S :S
Los campos bigint unsigned tienen 20 caracteres de capacidad (18.446.744.073.709.551.615 , http://www.desarrolloweb.com/articulos/1054.php), es decir, necesitarias 7 columnas de bigint donde en cada columna se guarden 20, y en la ultima 8.....

Perdón por la gracia...
¡Nada que ver!
En realidad en las bases de datos (MySQL en este caso), los números no existen como caracteres. Un BIGINT no usa 20 caracteres para almacenarse, usa 8 bytes, porque los números no se almacenan en caracteres sino como binarios.
Un BIGINT es un numero de 64 bits, por lo que si quieres guardar el valor binario de un IPV6 deberías usar 2 BIGINT nada más, uno para cada segmento de 64 bits, transformado en entero sin signo.
¿Se entiende la idea?
Si bien no existe una implementación actual que te genere el número desde una dirección IPV6, si existe para IPV4 (NTOA() y su inversa ATON()). Se estima que cuando se difunda completamente el IPV6 se habrá implementado para esta última.
Como sea, la cosa puede ser mucho más fácil de lo que piensas.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #3 (permalink)  
Antiguo 03/06/2011, 11:23
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

mmmmm, creo que no acabo de comprender....

Entonces te refieres a que si hago un insert solamente con la mitad de ese numero binario que puse arriba, es decir, el siguiente...:
11111110101111001010010101110100001110000010101100 1000111100000110101

¿Deberia guardarse?

Me sale un error indicandome que el numero es mas grande que el maximo permitido 18.446.744.073.709.551.615

:S

He encontrado una posible solucion, la posteo a continuacion...
  #4 (permalink)  
Antiguo 03/06/2011, 11:29
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

He hecho una prueba de ver cuanto ocupan 50 millones de registros en la estructura del primer mensaje de este hilo, usando el campo IP como varchar (128). El resultado es que ocupa 4,9 Gb.

Por otro lado acabo de hacer una prueba con una posible solucion que he encontrado. Consiste en que el campo IP sea decimal (39,0) unsigned. La misma cantidad de registros (50 millones) ocupan 2.6 Gb, es decir, practicamente la mitad....

Como he pasado una IP4 o IP6 a decimal? He usado un par de funciones que he encontrado, tb pongo la que devuelve de nuevo a la cadena IP original:

Código PHP:
Ver original
  1. function inet_ptod($ip_address)
  2. {
  3.     // IPv4 address
  4.     if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) {
  5.     $ip_address = '::' . $ip_address;
  6.     }
  7.     // IPv6 address
  8.     if (strpos($ip_address, ':') !== false) {
  9.     $network = inet_pton($ip_address);
  10.     $parts = unpack('N*', $network);
  11.     foreach ($parts as &$part) {
  12.     if ($part < 0) {
  13.     $part = bcadd((string) $part, '4294967296');
  14.     }
  15.     if (!is_string($part)) {
  16.     $part = (string) $part;
  17.     }
  18.     }
  19.     $decimal = $parts[4];
  20.     $decimal = bcadd($decimal, bcmul($parts[3], '4294967296'));
  21.     $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616'));
  22.     $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336'));
  23.     return $decimal;
  24.     }
  25.     // Decimal address
  26.     return $ip_address;
  27. }
  28. /**
  29.  * Convert an IP address from decimal format to presentation format
  30.  *
  31.  * @param string $decimal An IP address in IPv4, IPv6 or decimal notation
  32.  * @return string The IP address in presentation format
  33.  */
  34. function inet_dtop($decimal)
  35. {
  36.     // IPv4 or IPv6 format
  37.     if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) {
  38.     return $decimal;
  39.     }
  40.     // Decimal format
  41.     $parts = array();
  42.     $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0);
  43.     $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336'));
  44.     $parts[2] = bcdiv($decimal, '18446744073709551616', 0);
  45.     $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616'));
  46.     $parts[3] = bcdiv($decimal, '4294967296', 0);
  47.     $decimal = bcsub($decimal, bcmul($parts[3], '4294967296'));
  48.     $parts[4] = $decimal;
  49.     foreach ($parts as &$part) {
  50.     if (bccomp($part, '2147483647') == 1) {
  51.     $part = bcsub($part, '4294967296');
  52.     }
  53.     $part = (int) $part;
  54.     }
  55.     $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]);
  56.     $ip_address = inet_ntop($network);
  57.     // Turn IPv6 to IPv4 if it's IPv4
  58.     if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) {
  59.     return substr($ip_address, 2);
  60.     }
  61.     return $ip_address;
  62. }


Bueno, eso ahorra la mitad.... Es un buen paso no?, se podrá ahorrar todabia mas?? Parece mentira como un solo campo en solo 50 millones hace que algo ocupe el doble...
  #5 (permalink)  
Antiguo 03/06/2011, 11:30
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

Se me duplico el mensaje, edito esto por que no consigo borrar este mensaje :S
  #6 (permalink)  
Antiguo 03/06/2011, 11:34
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: IPV6 y IPV4 en campo INT

Porque esto:
Cita:
11111110101111001010010101110100001110000010101100 1000111100000110101
No es un binario... Eso es un entero de del orden 1 x 10E64^.
Lo que debes hacer es difidir la estructura en dos partes y determinar qué número de 64 bits representa, convertirlo a entero sin signo y almacenar eso.
o sea, si el número de 64 bits fuese: 11111110101111001010010101110100001110000010101100 10001111000001101011111111010111100101001010111010 0001110000010101100 1000111100000110101, lo que haces es dividirlo en dos partes:
11111110101111001010010101110100001110000010101100 1000111100000110101 /
11111110101111001010010101110100001110000010101100 1000111100000110101
Luego conviertes cada binario a entero sin signo: 18355728099832439745 / 18355728099832439745 (aproximado, la calculadora trabaja con signo) y almacenas en un campo cada uno.
Para recuerarlo debes hacer el paso inverso:
1) Recuperar el binario desde el entero.
2) Completarlo sin es necesario.
3) Recrear el binario de 128 bits.
4) Recuperar el IPV6.

¿Se entiende?
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #7 (permalink)  
Antiguo 03/06/2011, 12:04
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

He hecho la prueba, y mejora la solucion del decimal. He creado una tabla identica a la del primer mensaje del hilo, pero el campo IP que sea bigint, y he puesto otro llamado IP2 con big int.

Entonces he ejecutado este script:
Código PHP:
Ver original
  1. $fecha = time();
  2. $ip = '11111110101111001010010101110100001110000010101100100011110000011010101001001001010001011001001001001110111111101001100110000010';
  3. $parte1 = substr($ip, 0, 63);
  4. $parte2 = substr($ip, 64, 128);
  5.  
  6.  
  7. $n = 0;
  8. while($n < 200000){
  9. mysql_query("INSERT INTO acceso VALUES ('$id', '$fecha', 0, $fecha, '".bindec($parte1)."', '".bindec($parte2)."')");
  10. $n++;
  11. }


Esto ha insertado 200mil registros, ocupando 8.3 MB (que ha 50 millones son 1.9 GB)


Conclusion a 50 millones de registros:

Usando un campo varchar de 128 y guardando la ip en binario: 4.9 GB
Usando un campo decimal (39,0) y guardando la ip en decimal: 2.6 GB
Usando 2 campos BIGINT, convirtiendo la ip a binario, partiendo en 64 caracteres, y pasandolo a entero guardando cada parte en cada columna: 1.9 GB


Resultado: Mejor solucion los BIGINT de las propuestas! No se si estareis de acuerdo, pero almenos esa a sido mi conclusion tras el testeo.
  #8 (permalink)  
Antiguo 03/06/2011, 13:11
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

Creo que en mi proceso, el bindec no funciona :S
Por que he intentado reconstruirlo y los decbin de ese numero me devuelven cosas totalmente distintas, de hecho, la segunda parte pasandole directamente un decbin, de lo que en una variable acabo de pasar a bindec no funciona, me devuelve 0 :S


Valla jaleo!
  #9 (permalink)  
Antiguo 03/06/2011, 13:14
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

Creo que en mi proceso, el bindec no funciona :S
Por que he intentado reconstruirlo y los decbin de ese numero me devuelven cosas totalmente distintas, de hecho, la segunda parte pasandole directamente un decbin, de lo que en una variable acabo de pasar a bindec no funciona, me devuelve 0 :S

Alomejor es por que conincide que la segunda parte empieza con cero o alguna cosa rara de ese estilo


Valla jaleo!
  #10 (permalink)  
Antiguo 03/06/2011, 13:21
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: IPV6 y IPV4 en campo INT

La idea sería crear una función recursiva que recupere el binario desde un entero, pero ese tema excede los límites de este foro.
¿Por qué no consultas en el de PHP? Allí están los expertos en el lenguaje y seguro te pueden ayudar en esa parte.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #11 (permalink)  
Antiguo 03/06/2011, 15:39
 
Fecha de Ingreso: junio-2009
Mensajes: 309
Antigüedad: 15 años, 5 meses
Puntos: 5
Respuesta: IPV6 y IPV4 en campo INT

Funcion recursiva?? No entiendo para que haria falta.

Una funcion recursiva es una funcion que se llama a si misma, pero ¿para que haria falta aqui?
  #12 (permalink)  
Antiguo 03/06/2011, 15:49
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: IPV6 y IPV4 en campo INT

Para que te genere la cadena del binario correctamente expresada...
AL menos yo tenía en C, C++ y VB.Net funciones recursivas de pocas líneas que hacían precisamente eso.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)

Etiquetas: int, ipv4, ipv6, campos
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 09:14.