sql >> Base de Datos >  >> RDS >> Mysql

Buscar en direcciones IP parciales almacenadas como números enteros

En realidad, la columna de enteros sin signo ya es la forma más eficiente de buscar coincidencias en direcciones IP parciales. Por favor, no desperdicie su energía ni tiempo de CPU en volver a convertir a la notación punteada o buscar LIKE en algún tipo de columna de cadena.

Existen varias formas de escribir una dirección IP parcial, pero al final, todas se reducen a una IP base con una máscara de red. Además, suponiendo que parcial se refiere a todas las direcciones IP con un prefijo común, esto también es equivalente a especificar un rango de direcciones IP.

De cualquier manera, la especificación de la dirección IP parcial termina siendo descrita como dos números enteros sin firmar de 32 bits, codificados en el mismo formato que la columna de su base de datos. O tienes una ip inicial y una ip final, o tienes una ip base y una máscara. Estos enteros se pueden usar directamente dentro de su consulta SQL para obtener coincidencias de manera eficiente. Aún mejor, si usa el enfoque de rango de ip, entonces el motor podrá aprovechar un índice ordenado en su columna de ip. No puedes esperar nada mejor.

Entonces, ¿cómo construir el rango de IP? Dependerá de cómo se especificaron sus direcciones parciales en primer lugar, pero suponiendo que conoce la máscara de red, entonces la dirección de inicio es igual a (ip base y máscara de red), y la dirección final es ((ip base &máscara de red) | (~máscara de red)), donde &, | y ~ respectivamente significa bit a bit-y, bit a bit-o y bit a bit-no.

Actualizar

Aquí hay un código de muestra para aplicar la estrategia que describí.

Ahora, ha pasado mucho tiempo desde la última vez que escribí código PHP, y lo siguiente nunca se ha ejecutado, así que disculpe cualquier error que pueda haber introducido. También elegí deliberadamente "expandir" cada escenario de notación para hacerlos más fáciles de entender, en lugar de comprimirlos todos en una sola expresión regular muy compleja.

if (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [/] (\d{1,2}) $/x', $input, $r)) {
    // Four-dotted IP with number of significant bits: 123.45.67.89/24

    $a = intval($r[1]);
    $b = intval($r[2]);
    $c = intval($r[3]);
    $d = intval($r[4]);
    $mask = intval($r[5]);

} elseif (preg_match(' /^ (\d{1,3}) (?: [.] [*0] [.] [*0] [.] [*0] )? $/x', $input, $r)) {
    // Four-dotted IP with three-last numbers missing, or equals to 0 or '*':
    // 123.45, 123.45.0.0, 123.45.*.*  (assume netmask of 8 bits)

    $a = intval($r[1]);
    $b = 0;
    $c = 0;
    $d = 0;
    $mask = 8;

} elseif (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) (?: [.] [*0] [.] [*0] )? $/x', $input, $r)) {
    // Four-dotted IP with two-last numbers missing, or equals to 0 or '*':
    // 123.45, 123.45.0.0, 123.45.*.*  (assume netmask of 16 bits)

    $a = intval($r[1]);
    $b = intval($r[2]);
    $c = 0;
    $d = 0;
    $mask = 16;

} elseif (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) (?: [.] [*0] )? $/x', $input, $r)) {
    // Four-dotted IP with last number missing, or equals to 0 or *:
    // 123.45.67, 123.45.67.0, 123.45.67.*  (assume netmask of 24 bits)

    $a = intval($r[1]);
    $b = intval($r[2]);
    $c = intval($r[3]);
    $d = 0;
    $mask = 24;

} elseif (preg_match(' /^ (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) $/x', $input, $r)) {
    // Four-dotted IP: 123.45.67.89 (assume netmask of 32 bits)

    $a = intval($r[1]);
    $b = intval($r[2]);
    $c = intval($r[3]);
    $d = intval($r[4]);
    $mask = 32;

} else {
    throw new Exception('...');
}

if ($a < 0 || $a > 255) {  throw new Exception('...') };
if ($b < 0 || $b > 255) {  throw new Exception('...') };
if ($c < 0 || $c > 255) {  throw new Exception('...') };
if ($d < 0 || $d > 255) {  throw new Exception('...') };
if ($mask < 1 || $mask > 32) {  throw new Exception('...') };

$baseip = ($a << 24) + ($b << 16) + ($c << 8) + ($d);
$netmask = (1 << (32 - $mask)) - 1;

$startip = $baseip & netmask;
$endip = ($baseip & netmask) | (~netmask);

// ...

doSql( "SELECT ... FROM ... WHERE ipaddress >= ? && ipaddress <= ?", $startip, $endip);

// or

doSql( "SELECT ... FROM ... WHERE ((ipaddress & ?) = ?)", $netmask, $startip);