Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Pesquise em endereços IP parciais armazenados como números inteiros


Na verdade, a coluna de inteiros não assinados já é a maneira mais eficiente de procurar correspondências em endereços IP parciais! Por favor, não desperdice sua energia nem tempo de CPU convertendo de volta para notação pontilhada ou indo para uma pesquisa LIKE em algum tipo de coluna de string.

Existem várias maneiras de escrever endereços IP parciais, mas no final, todos eles se resumem a um ip base com uma máscara de rede. Além disso, assumindo que por parcial, você quer dizer todos os IPs com um prefixo comum, então isso também é equivalente a especificar um intervalo de IPs.

De qualquer forma, a especificação de endereço IP parcial acaba sendo descrita como dois inteiros sem sinal de 32 bits, codificados no mesmo formato da coluna do banco de dados. Ou você tem um ip inicial e um ip final, ou você tem um ip base e uma máscara. Esses números inteiros podem ser usados ​​diretamente dentro de sua consulta SQL para obter correspondências com eficiência. Melhor ainda, se você usar a abordagem de intervalo de ip, o mecanismo poderá tirar proveito de um índice ordenado em sua coluna de ip. Você não pode esperar nada melhor.

Então, como construir o intervalo de IP? Isso depende de como seus endereços parciais foram especificados em primeiro lugar, mas supondo que você conheça a máscara de rede, o endereço inicial é igual a (ip base e máscara de rede) e o endereço final é ((ip base &máscara de rede) | (~máscara de rede)), onde &, | e ~ respectivamente significa bit a bit e, bit a bit ou e bit a bit não.

Atualizar

Aqui está um código de exemplo para aplicar a estratégia que descrevi.

Agora, faz muito tempo desde a última vez que escrevi código PHP, e o seguinte nunca foi executado, então, por favor, desculpe qualquer erro que eu possa ter introduzido. Também escolhi deliberadamente "expandir" cada cenário de notação para torná-los mais fáceis de entender, em vez de espremer todos eles em um único regex muito complexo.
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);