web-dev-qa-db-fra.com

Déterminer si une adresse IP est dans un bloc IPv4 CIDR

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.

5
RMathis

PostgreSQL

À 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.

2
Evan Carroll

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.

2
Rick James