Mysql
 sql >> Database >  >> RDS >> Mysql

Cerca su indirizzi IP parziali memorizzati come numeri interi

In realtà, la colonna intera senza segno è già il modo più efficiente per cercare corrispondenze su indirizzi IP parziali! Per favore, non sprecare energia né tempo della CPU per riconvertire la notazione puntata o per eseguire una ricerca LIKE su una sorta di colonna di stringhe.

Esistono diversi modi per annotare l'indirizzo IP parziale, ma alla fine si riducono tutti a un IP di base con una maschera di rete. Inoltre, supponendo che per parziale intendi tutti gli IP con un prefisso comune, questo equivale anche a specificare un intervallo di IP.

In ogni caso, la specifica dell'indirizzo IP parziale finisce per essere descritta come due 32 bit, interi senza segno, codificati nello stesso formato della colonna del database. O hai un IP iniziale e un IP finale, oppure hai un IP di base e una maschera. Questi numeri interi possono essere utilizzati direttamente all'interno della query SQL per ottenere corrispondenze in modo efficiente. Ancora meglio, se utilizzi l'approccio dell'intervallo ip, il motore sarà in grado di sfruttare un indice ordinato sulla colonna ip. Non puoi aspettarti di meglio.

Quindi, come costruire l'intervallo IP? Dipenderà da come sono stati specificati i tuoi indirizzi parziali in primo luogo, ma supponendo che tu conosca la maschera di rete, l'indirizzo iniziale è uguale a (ip di base e maschera di rete) e l'indirizzo finale è ((ip di base &net mask) | (~netmask)), dove &, | e ~ rispettivamente significa bit per bit e, bit per bit o e non per bit.

Aggiorna

Ecco un codice di esempio per applicare la strategia che ho descritto.

Ora, è passato molto tempo dall'ultima volta che ho scritto il codice PHP e quanto segue non è mai stato eseguito, quindi scusa qualsiasi errore che potrei aver introdotto. Ho anche scelto deliberatamente di "espandere" ogni scenario di notazione per renderli più facili da capire, piuttosto che comprimerli tutti in un'unica espressione regolare molto complessa.

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);