3 votes

Manière appropriée de trouver IPv6 dans la plage CIDR MySQL

Je dispose d'une table de liste noire MySQL, avec des plages IPv4, IPv6 ou CIDR simples de l'un ou l'autre type stockées dedans.

Ma table ressemble plus ou moins à ceci :

 +-----------+-------------+
 | Nom       | Type        |
 +-----------+-------------+
 | AdresseIp | VARCHAR(46) |
 +-----------+-------------+
 | Masque    | INT(2)      |
 +-----------+-------------+

Maintenant, j'aimerais vérifier si une adresse IP donnée se trouve dans l'une des plages CIDR enregistrées. J'ai déjà accompli cela après quelques jours de recherche sur de nombreuses pages, et j'ai finalement simplement adapté la solution suivante à MySQL : PHP5 calculate IPv6 range from cidr prefix?

Ainsi, je transforme une adresse IP + masque CIDR en la première et la dernière adresse IP de cette plage, que je convertis ensuite en nombres en utilisant INET6_ATON et que je compare avec l'opérateur BETWEEN.

Mon implémentation :

Obtenir la dernière adresse IP

FONCTION (`Ip` VARCHAR(46), `Masque` INT(2) UNSIGNED) RETOURNE varchar(39)
DEBUT
    DECLARE IpNumber VARBINARY(16);
    DECLARE Dernier VARCHAR(39) DEFAULT '';
    DECLARE FlexBits, Counter, Deci, NewByte INT UNSIGNED;
    DECLARE HexIp VARCHAR(32);

    SET IpNumber = INET6_ATON(Ip);
    SET HexIp    = HEX(IpNumber);
    SET FlexBits = 128 - Masque;
    SET Counter  = 32;

    TANT QUE (FlexBits > 0) FAIRE
        SET Deci    = CONV(SUBSTR(HexIp, Counter, 1), 16, 10);
        SET NewByte = Deci | (POW(2, LEAST(4, FlexBits)) - 1);
        SET Dernier = CONCAT(CONV(NewByte, 10, 16), Dernier);

        IF FlexBits >= 4 THEN SET FlexBits = FlexBits - 4;
        ELSE SET FlexBits = 0;
        END IF;

        SET Counter  = Counter - 1;
    FIN TANT QUE;

    SET Dernier = CONCAT(SUBSTR(HexIp, 1, Counter), Dernier);

    RETURN INET6_NTOA(UNHEX(Dernier));
FIN

Obtenir la première adresse IP

FONCTION (`Ip` VARCHAR(46), `Masque` INT(2) UNSIGNED, `AvecMasque` BOOLEAN) RETOURNE varchar(39)
DEBUT
    DECLARE Premier VARCHAR (42) DEFAULT '';

    SET Premier = INET6_NTOA(UNHEX(RPAD(SUBSTR(HEX(INET6_ATON(Ip)), 1, Masque / 4), 32, 0)));

    SI (AvecMasque = 1) ALORS
        SET Premier = CONCAT(Premier, '/', CAST(Masque AS CHAR));
    FIN SI;

    RETURN Premier;
FIN

Cela fonctionne bien ! J'ai juste l'idée que cela pourrait être fait de manière beaucoup plus efficace en utilisant des opérations de bits astucieuses. J'ai lu des tonnes de questions sur ce sujet, mais je n'ai pas vraiment trouvé de solution concrète. Toute aide dans la bonne direction serait vraiment appréciée !

REMARQUE : Seulement pour IPv6, j'ai correctement mis en œuvre IPv4.

1voto

Zoltán Süle Points 359

J'ai trouvé un bug dans votre fonction Récupérer dernière IP.

au lieu de :

SET FlexBits = FlexBits - 4;

utilisez ceci :

SI FlexBits >= 4 ALORS SET FlexBits = FlexBits - 4;
SINON SET FlexBits = 0;
FIN SI;

Sinon, vous obtiendrez ce genre de message d'erreur :

ERREUR 1690 (22003) : valeur BIGINT UNSIGNED est hors de portée dans '(FlexBits@4 - 4)'

Lorsque vous essayez d'obtenir la dernière IP de ces plages IPv6 (où le masque ne peut pas être divisé par 4) :

SELECT getLastIp("2001:200:1::", 47);
SELECT getLastIp("2001:200:0:8000", 49);
SELECT getLastIp("2001:470:1f06:2000::", 51);
SELECT getLastIp("2001:470:0:284:2::", 79);
SELECT getLastIp("2001:470:1f08:415::8:0", 109);
SELECT getLastIp("2001:550:0:1000::8275:8000", 113);
SELECT getLastIp("2001:550:0:1000::9a19:300", 123);
SELECT getLastIp("2001:550:0:1000::9a19:320", 126);

1voto

manuel Points 11

J'ai implémenté cela pour MySQL de la même manière il y a environ un an. Donc l'idée est bonne. Cependant, il y a un petit mais assez significatif bug dans votre code.

La ligne :

SET Last = CONCAT(Last, CONV(NewByte, 10, 16));

devrait plutôt être :

SET Last = CONCAT(CONV(NewByte, 10, 16), Last);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X