Quel est le moyen le plus rapide de déterminer si une adresse IP est contenue dans un bloc CIDR?
Pour le moment, chaque fois que je stocke une adresse CIDR, je crée également deux colonnes pour démarrer et mettre fin aux adresses IP. Les adresses IP de départ et de fin sont indexées. Si je veux voir quel réseau contient une adresse, je cherche where ip between start_ip and end_ip
qui semble moins que souhaitable.
Il me semble que je puisse stocker le bon numéro décalé et peut correspondre à une adresse IP de la même manière (660510 dans le cas de @CIDR) ...
select @cidr, inet_aton(substring_index(@cidr,'/',1))>>(32-substring_index(@cidr,'/',-1));
+---------------+-----------------------------------------------------------------------------+
| @cidr | inet_aton(substring_index(@cidr,'/',1))>>(32-substring_index(@cidr,'/',-1)) |
+---------------+-----------------------------------------------------------------------------+
| 10.20.30.0/24 | 660510 |
+---------------+-----------------------------------------------------------------------------+
1 row in set (0.00 sec)
set @ip:='10.20.30.40';
Query OK, 0 rows affected (0.00 sec)
select @ip, inet_aton(@ip)>>(32-substring_index(@cidr,'/',-1));
+-------------+----------------------------------------------------+
| @ip | inet_aton(@ip)>>(32-substring_index(@cidr,'/',-1)) |
+-------------+----------------------------------------------------+
| 10.20.30.40 | 660510 |
+-------------+----------------------------------------------------+
1 row in set (0.00 sec)
Afin de tirer parti de cela de manière indexée, je devrais connaître le masque de sous-réseau (le nombre de bits à décaler). Sinon, je serai soit systématiquement comparer des changements de bits (c'est-à-dire aveuglément pour chaque masque netmasque possible (de 0 à 24 bits)).
J'ai d'autres sources pour optimiser, mais optimiser la base de données IP -Location IP-ASN IP2Location ™ Lite trouvée à http://lite.ip2location.com/database/ip-asn serait une preuve de concept.
La table...
CREATE TABLE `ip2loc_asn` (
`asn` bigint(20) DEFAULT NULL,
`cidr` varchar(50) DEFAULT NULL,
`start_ip` bigint(20) DEFAULT NULL,
`end_ip` bigint(20) DEFAULT NULL,
`name` varchar(250) DEFAULT NULL,
KEY `ip2locasn_startip_endip` (`start_ip`,`end_ip`),
KEY `asn` (`asn`),
KEY `cidr` (`cidr`)
) ENGINE=MyISAM; -- table is recreated monthly, MyISAM is the perfect engine
Exemples de données ...
select * from ip2loc_asn limit 10;
+-------+--------------+----------+----------+-------------------------------+
| asn | cidr | start_ip | end_ip | name |
+-------+--------------+----------+----------+-------------------------------+
| 56203 | 1.0.4.0/24 | 16778240 | 16778495 | Big Red Group |
| 56203 | 1.0.5.0/24 | 16778496 | 16778751 | Big Red Group |
| 56203 | 1.0.6.0/24 | 16778752 | 16779007 | Big Red Group |
| 38803 | 1.0.7.0/24 | 16779008 | 16779263 | Goldenit Pty ltd Australia, A |
| 18144 | 1.0.64.0/18 | 16793600 | 16809983 | Energia Communications,Inc. |
| 9737 | 1.0.128.0/17 | 16809984 | 16842751 | TOT Public Company Limited |
| 9737 | 1.0.128.0/18 | 16809984 | 16826367 | TOT Public Company Limited |
| 9737 | 1.0.128.0/19 | 16809984 | 16818175 | TOT Public Company Limited |
| 23969 | 1.0.128.0/24 | 16809984 | 16810239 | TOT Public Company Limited |
| 23969 | 1.0.129.0/24 | 16810240 | 16810495 | TOT Public Company Limited |
+-------+--------------+----------+----------+-------------------------------+
10 rows in set (0.00 sec)
Netmask varie de 8 à 32 bits ...
select min(substring_index(cidr,'/',-1)+0), max(substring_index(cidr,'/',-1)+0) from ip2loc_asn;
+-------------------------------------+-------------------------------------+
| min(substring_index(cidr,'/',-1)+0) | max(substring_index(cidr,'/',-1)+0) |
+-------------------------------------+-------------------------------------+
| 8 | 32 |
+-------------------------------------+-------------------------------------+
1 row in set (0.33 sec)
select * from ip2loc_asn where cidr like '%/8' limit 1;
+------+-----------+----------+----------+------------------------------+
| asn | cidr | start_ip | end_ip | name |
+------+-----------+----------+----------+------------------------------+
| 3356 | 4.0.0.0/8 | 67108864 | 83886079 | Level 3 Communications, Inc. |
+------+-----------+----------+----------+------------------------------+
1 row in set (0.00 sec)
select * from ip2loc_asn where cidr like '%/32' limit 1;
+-------+---------------+-----------+-----------+------+
| asn | cidr | start_ip | end_ip | name |
+-------+---------------+-----------+-----------+------+
| 51964 | 57.72.27.1/32 | 961026817 | 961026817 | |
+-------+---------------+-----------+-----------+------+
1 row in set (0.02 sec)
Plan d'exécution actuel ...
explain select * from ip2loc_asn where inet_aton('10.20.30.40') between start_ip and end_ip;
+----+-------------+------------+-------+--------------------------+--------------------------+---------+------+-------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+--------------------------+--------------------------+---------+------+-------+-----------------------+
| 1 | SIMPLE | ip2loc_asn | range | ip2loc_asn_startip_endip | ip2loc_asn_startip_endip | 9 | NULL | 10006 | Using index condition |
+----+-------------+------------+-------+--------------------------+--------------------------+---------+------+-------+-----------------------+
1 row in set (0.00 sec)
Ma tentative maladroite ...
mysql to3_reference> alter table ip2loc_asn add column shifted_netmask int(10) unsigned;
Query OK, 626695 rows affected (4.06 sec)
Records: 626695 Duplicates: 0 Warnings: 0
mysql to3_reference> update ip2loc_asn set shifted_netmask = start_ip>>(32-substring_index(cidr,'/',-1));
Query OK, 626695 rows affected (5.98 sec)
Rows matched: 626695 Changed: 626695 Warnings: 0
mysql to3_reference> alter table ip2loc_asn add key ip2loc_asn_shiftednetmask (shifted_netmask);
Query OK, 626695 rows affected (5.83 sec)
Records: 626695 Duplicates: 0 Warnings: 0
Viette:
select * from ip2loc_asn where inet_aton('8.8.8.0') between start_ip and end_ip;
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| asn | cidr | shifted_netmask | netmask_bits | start_ip | end_ip | name |
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| 3356 | 8.0.0.0/9 | 16 | 9 | 134217728 | 142606335 | Level 3 Communications, Inc. |
| 3356 | 8.0.0.0/8 | 8 | 8 | 134217728 | 150994943 | Level 3 Communications, Inc. |
| 15169 | 8.8.8.0/24 | 526344 | 24 | 134744064 | 134744319 | Google Inc. |
+-------+------------+-----------------+--------------+-----------+-----------+- -----------------------------+
3 rows in set (0.00 sec)
Une approche utilisant Changed_netmask (indésirable - Je supporte une numérisation de table complète pour découvrir le nombre de bits dans Netmask) ...
select * from ip2loc_asn where shifted_netmask = inet_aton('8.8.8.0')>>32-netmask_bits;
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| asn | cidr | shifted_netmask | netmask_bits | start_ip | end_ip | name |
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
| 3356 | 8.0.0.0/8 | 8 | 8 | 134217728 | 150994943 | Level 3 Communications, Inc. |
| 3356 | 8.0.0.0/9 | 16 | 9 | 134217728 | 142606335 | Level 3 Communications, Inc. |
| 15169 | 8.8.8.0/24 | 526344 | 24 | 134744064 | 134744319 | Google Inc. |
+-------+------------+-----------------+--------------+-----------+-----------+------------------------------+
3 rows in set (0.64 sec)
L'approche souhaitée est similaire à la dernière requête moins la numérisation des bits Netmask.
À titre de note latérale postgreSQL, le fait-il hors de l'étagère avec cidr
et inet
types . Et si vous voulez vraiment faire ce travail haut de gamme, regardez dans le ip4r
Il me semble que je puisse stocker le bon numéro décalé et peut correspondre à une adresse IP de la même manière (660510 dans le cas de @CIDR) ...
Bonne pensée, c'est en fait comment PostgreSQL les stocke en interne . Facilement fait,
CREATE TABLE ip2loc_asn (
asn bigint,
cidr cidr,
name text
);
CREATE INDEX ON ip2loc_asn USING Gist(cidr);
INSERT INTO ip2loc_asn(asn,cidr,name)
VALUES
( 56203, '1.0.4.0/24' , 'Big Red Group' ),
( 56203, '1.0.5.0/24' , 'Big Red Group' ),
( 56203, '1.0.6.0/24' , 'Big Red Group' ),
( 38803, '1.0.7.0/24' , 'Goldenit Pty ltd Australia, A' ),
( 18144, '1.0.64.0/18' , 'Energia Communications,Inc.' ),
( 9737, '1.0.128.0/17' , 'TOT Public Company Limited' ),
( 9737, '1.0.128.0/18' , 'TOT Public Company Limited' ),
( 9737, '1.0.128.0/19' , 'TOT Public Company Limited' ),
( 23969, '1.0.128.0/24' , 'TOT Public Company Limited' ),
( 23969, '1.0.129.0/24' , 'TOT Public Company Limited' );
Maintenant, nous pouvons la demander avec les opérateurs de type résea
test=# SELECT * FROM ip2loc_asn WHERE cidr >> '1.0.129.0';
asn | cidr | name
-------+--------------+----------------------------
9737 | 1.0.128.0/17 | TOT Public Company Limited
9737 | 1.0.128.0/18 | TOT Public Company Limited
9737 | 1.0.128.0/19 | TOT Public Company Limited
23969 | 1.0.129.0/24 | TOT Public Company Limited
Cela se produit aussi sur l'index.
Le principal problème est que l'optimiseur n'a aucune connaissance, qu'il existe une paire de démarrage qui correspond, ou beaucoup. Ainsi, toute tentative d'optimisation est bloquée avec une numérisation de table ou au moins une balayage de grande gamme.
Pour qui devez-vous commencer? Adresses IP? Ou des blocs de la CIDR? Je demande cela parce que nous devrions peut-être réorganiser les données que vous commencez avec de manière à effectuer des recherches de l'autre efficacement.
Dans Ceci , j'explique comment construire et maintenir une table des adresses IP 2 ^ 32 (ou IPv6). Il utilise seulement un start_ip
colonne et infère end_ip
de la rangée suivante. Cela implique que toutes les gammes IP non attribuées doivent avoir une ligne dans la table. (Ce n'est pas un gros fardeau, au plus doublant le nombre de lignes.) Avec cela, pratiquement toutes les opérations sont essentiellement O(1) - c'est-à-dire quelque chose comme WHERE ip >= start_ip ORDER BY start_ip DESC LIMIT 1
obtient la réponse "immédiatement". Pas de balayage de table, pas de balayage de plage; rien pire qu'une "requête ponctuelle" (efficacement). Notez qu'il n'a même pas besoin de tester fin_ip. CAVEZER: Les gammes qui se chevauchent ne sont pas manipulées. Certaines applications (peut-être pas les vôtres) peuvent être adaptées pour ne pas avoir besoin de chevauchement.
Comment adapter cela à la CIDR? Une façon est de transformer votre table des CIDRS dans ma variante. Vous connaissez comment faire cela; Les principales différences étant le manque de find_IP et l'ajout de gammes "détenues". Donc, si vous "commencez par" CIDRS et avez besoin de chercher des IPS, c'est une réponse possible.