Mysql
 sql >> Base de données >  >> RDS >> Mysql

Recherche sur des adresses IP partielles stockées sous forme d'entiers

En fait, la colonne d'entiers non signés est déjà le moyen le plus efficace de rechercher des correspondances sur des adresses IP partielles ! S'il vous plaît, ne gaspillez pas votre énergie ni votre temps CPU à reconvertir en notation pointée ou à effectuer une recherche LIKE sur une sorte de colonne de chaîne.

Il existe plusieurs façons d'écrire une adresse IP partielle, mais au final, elles se résument toutes à une adresse IP de base avec un masque de réseau. De plus, en supposant que par partiel, vous entendez toutes les adresses IP avec un préfixe commun, cela équivaut également à spécifier une plage d'adresses IP.

Dans tous les cas, la spécification de l'adresse IP partielle finit par être décrite comme deux entiers non signés de 32 bits, codés dans le même format que la colonne de votre base de données. Soit vous avez une adresse IP de départ et une adresse IP de fin, soit vous avez une adresse IP de base et un masque. Ces entiers peuvent être utilisés directement dans votre requête SQL pour obtenir des correspondances efficacement. Mieux encore, si vous utilisez l'approche par plage d'adresses IP, le moteur pourra tirer parti d'un index ordonné sur votre colonne IP. Vous ne pouvez pas vous attendre à mieux.

Alors, comment construire la plage IP ? Cela dépendra de la façon dont vos adresses partielles ont été spécifiées en premier lieu, mais en supposant que vous connaissiez le masque réseau, l'adresse de début est égale à (base ip &net mask) et l'adresse de fin est ((base ip &masque de réseau) | (~masque de réseau)), où &, | et ~ signifie respectivement bitwise-and, bitwise-or et bitwise-not.

Mettre à jour

Voici un exemple de code pour appliquer la stratégie que j'ai décrite.

Maintenant, cela fait très longtemps que j'ai écrit du code PHP pour la dernière fois, et ce qui suit n'a jamais été exécuté, alors veuillez excuser toute erreur que j'aurais pu introduire. J'ai également choisi délibérément de "développer" chaque scénario de notation afin de les rendre plus faciles à comprendre, plutôt que de les serrer tous dans une seule expression régulière très complexe.

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