2009-02-27 12 views
17

Algo así como¿Hay forma de hacer coincidir la IP con IP + CIDR directamente desde la consulta SELECT?

SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';

para que no traiga primero todos los registros de base de datos y luego coincide con ellos uno por uno.

Si c> 0 se hicieron coincidir. tabla

BANS:

id int auto incr PK 
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6) 
ipaddr BINARY(128) 
cidr INT 
host VARCHAR(255) 

DB: MySQL 5

IP y el tipo IPV (4 o 6) es conocido cuando se consulta.

IP es, por ejemplo :: 1 en el formato binario

BANNED IP es, por ejemplo :: 1/64

+0

Raspi, Sé que esta pregunta es viejo, pero ... es que su columna cidr el número de uno-bits de la máscara de red, por lo que para IPv6 siempre es 64 y para IPv4 es un número entre 0 y 32? Aunque supongo que 0 prohibiría todas las direcciones ... :) – ErikE

Respuesta

22

Recuerde que los PI no son una dirección de texto, sino una identificación numérica. Tengo una situación similar (estamos haciendo búsquedas geo-ip), y si almacena todas sus direcciones IP como enteros (por ejemplo, mi dirección IP es 192.115.22.33, por lo que se almacena como 3228767777), entonces puede buscar direcciones IP fácilmente utilizando operadores de desplazamiento a la derecha.

La desventaja de todos estos tipos de búsquedas es que no puede beneficiarse de los índices y debe realizar una exploración de tabla completa cada vez que realiza una búsqueda. El esquema anterior se puede mejorar almacenando tanto la dirección IP de red de la red CIDR (el comienzo del rango) como la dirección de difusión (el final del rango), por ejemplo, para almacenar 192.168.1.0/24 puede almacenar dos columnas:

network  broadcast 
3232235776, 3232236031 

y luego se puede para que coincidan sólo tiene que hacer

SELECT count(*) FROM bans WHERE 3232235876 >= network AND 3232235876 <= broadcast 

Esto permitirá almacenar redes CIDR en la base de datos y el partido contra direcciones IP de forma rápida y eficiente mediante el aprovechamiento de rápida índices numéricos.

Nota de discusión a continuación:

MySQL 5.0 incluye una optimización de la consulta a distancia denominado "index merge intersect", que permite agilizar este tipo de consultas (y evitar escaneos completos de tabla), siempre y cuando:

  • Hay un índice de varias columnas que coincide exactamente con las columnas de la consulta, en orden. Entonces, para el ejemplo de consulta anterior, el índice debería ser (network, broadcast).
  • Todos los datos se pueden recuperar del índice. Esto es cierto para COUNT(*), pero no es cierto para SELECT * ... LIMIT 1.

MySQL 5.6 incluye una optimización llamada MRR que también aceleraría la recuperación de filas completas, pero eso está fuera del alcance de esta respuesta.

+0

Desafortunadamente, MySQL no puede combinar dos índices juntos. Por supuesto, tratará de usar el índice en la red o en la transmisión, pero como las direcciones IP se distribuyen de manera uniforme, la exploración de tabla completa será mucho más eficiente en este caso. – Quassnoi

+0

Este mito está un poco desactualizado :-) comenzando con MySQL 5.0, el servidor puede combinar múltiples índices (http://dev.mysql.com/doc/refman/5.1/en/index-merge-optimization.html) . Independientemente, no puedo ver cómo un escaneo de tabla completo es mejor que usar un índice, incluso si es solo un índice. – Guss

+0

Si su filtro devuelve más de aproximadamente 10% o filas, la exploración de tabla completa es mejor. Pruébalo :) – Quassnoi

0

Hmmm. Puede construir una tabla de las máscaras de cidr, unirla y luego comparar el IP y el IP (& en MySQL) con la máscara con el bloque de prohibición ipaddress. ¿Eso haría lo que quieres?

Si no desea construir una tabla de máscara, puede calcular la máscara como -1 << (x-cidr) con x = 64 o 32 dependiendo.

3

Para IPv4, puede utilizar:

SET @length = 4; 

SELECT INET_NTOA(ipaddr), INET_NTOA(searchaddr), INET_NTOA(mask) 
FROM (
    SELECT 
     (1 << (@length * 8)) - 1 & ~((1 << (@length * 8 - cidr)) - 1) AS mask, 
     CAST(CONV(SUBSTR(HEX(ipaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS ipaddr, 
     CAST(CONV(SUBSTR(HEX(@myaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS searchaddr 
    FROM ip 
) ipo 
WHERE ipaddr & mask = searchaddr & mask 
+0

Gracias. Estaba bastante seguro de que no necesitaba una tabla para hacer CIDR, pero podría calcular/crear las máscaras sobre la marcha de esta manera. Creo que la máscara se puede simplificar a ~ ((1 << cidr) -1), donde ((1 << cidr) -1) establece que los bits cidr más a la derecha son verdaderos, y el NOT, IE: el ~ revierte todos los bits en el largo sin signo para que los bits cidr más a la derecha sean falsos y todos los que estén a la izquierda sean verdaderos. El Win7 + calc, usando la interfaz del Programador, es bastante útil en estos asuntos. – RocketRoy

-2

MySQL: Convertir rango de IP a CIDR

pasé varias horas en busca de una manera de utilizar MySQL para tener un rango de direcciones IP y salida de una dirección con el formato CIDR que cubre el rango de IP adecuada para mantener una lista negra de IP .

Detalles de mi entorno y requisitos Utilizo OpenWeb Analytics para registrar el tráfico en mi sitio web y varios que administro. Desarrollé un proceso para extraer distintas direcciones IP registradas por OWA y luego fusionar elementos de datos geográficos en los registros que luego se mantienen en una tabla personalizada. Armado con esta tabla de datos de IP a ubicación proporciona la capacidad de informar sobre aciertos de fuentes no deseadas, o fuentes irrelevantes para mi negocio local: China, Japón, Corea, Rusia, etc. Esto ha llevado a una creciente lista de IP direcciones; muchos de los cuales caen dentro de la misma red. Para facilitar los requisitos de mantenimiento para el archivo .htaccess de mi servidor, resulta beneficioso poder registrar direcciones IP en la lista negra en formato CIDR. Eso lleva a la necesidad de poder y producir el CIDR a partir de las direcciones IP ya registradas.

Enfoque de MySQL La mayoría de los servidores web proporcionan acceso a bases de datos MySQL. Sin embargo, pocos o ninguno permiten la capacidad de crear funciones de base de datos. Esto complicó un poco la codificación. SQL

Muestra disponible en http://blog.watsoninfotech.com/2012/12/mysql-convert-ip-range-to-cidr.html

+1

Tengo problemas para ver cómo está * directamente * relacionado con la pregunta. –

2

direcciones IPv4, las direcciones de red y máscaras de red son todos UINT32 números y se presentan en forma legible por humanos como "punteadas-quads". El código de la tabla de enrutamiento en el kernel realiza una comparación Y de bits muy rápida cuando se comprueba si una dirección se encuentra en un espacio de red dado (red/máscara de red). El truco aquí es almacenar las direcciones IP cuadriculadas, las direcciones de red y las máscaras de red en sus tablas como UINT32, y luego realizar la misma Y de 32 bits para su correspondencia. por ejemplo

SET @test_addr = inet_aton('1.2.3.4'); 
SET @network_one = inet_aton('1.2.3.0'); 
SET @network_two = inet_aton('4.5.6.0'); 
SET @network_netmask = inet_aton('255.255.255.0'); 

SELECT (@test_addr & @network_netmask) = @network_one AS IS_MATCHED; 
+------------+ 
| IS_MATCHED | 
+------------+ 
|   1 | 
+------------+ 

SELECT (@test_addr & @network_netmask) = @network_two AS IS_NOT_MATCHED; 
+----------------+ 
| IS_NOT_MATCHED | 
+----------------+ 
|    0 | 
+----------------+ 
Cuestiones relacionadas