2008-11-10 5 views
11

Je voudrais trouver un moyen de faire une requête SQL qui calcule la cidr (représentation bit) d'un masque de sous-réseau stocké dans la base de données. Ainsi, par exemple, j'ai 255.255.255.0 ou sa valeur décimale (4294967040) stockée dans la base de données. Je voudrais faire une sélection et revenir/24 représentation via la requête.Utilisation de SQL pour déterminer la valeur cidr d'un masque de sous-réseau

J'ai fait des choses comme les suivantes pour déterminer la dernière adresse IP d'un sous-réseau, donc j'espère faire quelque chose de similaire pour déterminer la représentation cidr d'un masque.

select concat(inet_ntoa(ip_addr),'-', 
    inet_ntoa(ip_addr+(POWER(2,32)-ip_mask-1))) range 
from subnets 
order by ip_addr 

De préférence, ce serait une instruction SQL qui travaillerait sous MySQL, Postgres, Oracle, etc.

Répondre

8

Je pense que j'ai trouvé la solution à mon problème. Voici ce que je l'ai fait:

select CONCAT(INET_NTOA(ip_addr),'/',32-log2((4294967296-ip_mask))) net 
from subnets 
order by ip_addr 

Fondamentalement, je prends mon masque decmial et soustraire de la valeur décimale maximale. Je puis à un log2 sur cette valeur pour obtenir la valeur logarithmique. Il suffit ensuite de soustraire cela de 32 (le bit maximum disponible).

L'espoir qui aide les autres.

Merci

+0

log2 ... génial! :-) –

+0

J'ai vérifié cela par rapport à toutes les valeurs de ce tableau et je l'ai trouvé exact: https://kthx.at/subnetmask/. – apostl3pol

2

requêtes SQL ne sont pas une construction de looping procédure (nonobstant la langue de procédure), mais vous pouvez comparer un ensemble de lignes à un autre ensemble de lignes, ce qui est un peu comme une boucle.

Vous avez seulement 32 masques de sous-réseau possibles. Dans de tels cas, il est logique de créer une petite table qui stocke ces 32 masques et le numéro CIDR associé.

CREATE TABLE cidr (
    bits INT UNSIGNED PRIMARY KEY, 
    mask INT UNSIGNED NOT NULL 
); 

INSERT INTO cidr (bits) VALUES 
    (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), 
    (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), 
    (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), 
    (31), (32); 

UPDATE cidr SET mask = ((POWER(2,32)-1)<<(32-bits)) & (POWER(2,32)-1); 

SELECT CONCAT(s.ip_addr, '/', c.bits) 
FROM cidr c JOIN subnets s ON (c.mask = inet_aton(s.ip_mask)); 
+0

Ajout d'une table était ce que j'avait espéré éviter. J'espérais qu'il y aurait d'autres calculs mathématiques qui pourraient être faits pour convertir de décimal en binaire, puis compter les bits. Merci quand même, c'était utile –

0
-- 
-- Dumping routines for database 
-- 
/*!50003 DROP FUNCTION IF EXISTS `INET_ATOC` */; 
/*!50003 SET @saved_cs_client  = @@character_set_client */ ; 
/*!50003 SET @saved_cs_results  = @@character_set_results */ ; 
/*!50003 SET @saved_col_connection = @@collation_connection */ ; 
/*!50003 SET character_set_client = utf8 */ ; 
/*!50003 SET character_set_results = utf8 */ ; 
/*!50003 SET collation_connection = utf8_general_ci */ ; 
/*!50003 SET @saved_sql_mode  = @@sql_mode */ ; 
/*!50003 SET sql_mode    = 'ALLOW_INVALID_DATES' */ ; 
DELIMITER ;; 
CREATE DEFINER=`root`@`localhost` FUNCTION `INET_ATOC`(`paramNETMASK` varchar(15)) RETURNS int(2) unsigned 
    DETERMINISTIC 
    COMMENT 'Converts an IPv4 netmask in dotted decimal notation to a CIDR integer between 0 and 32' 
BEGIN 
    DECLARE `netmask` int unsigned; 
    DECLARE `cidr` int unsigned; 
    SET `netmask` = INET_ATON(`paramNETMASK`); 
    IF (`netmask` IS NULL) 
    THEN 
     RETURN NULL; 
    ELSE 
     SET `cidr` = 0; 
     countNetBits: WHILE (`cidr` < 32) 
     DO 
      IF ((0x80000000 & `netmask`) = 0x80000000) 
      THEN 
       SET `netmask` = 0xFFFFFFFF & (`netmask` << 1); 
       SET `cidr` = `cidr` + 1; 
      ELSE 
       LEAVE countNetBits; 
      END IF; 
     END WHILE; 
     IF (`netmask` != 0) 
     THEN 
      RETURN NULL; 
     END IF; 
     RETURN `cidr`; 
    END IF; 
END ;; 
DELIMITER ; 
/*!50003 SET sql_mode    = @saved_sql_mode */ ; 
/*!50003 SET character_set_client = @saved_cs_client */ ; 
/*!50003 SET character_set_results = @saved_cs_results */ ; 
/*!50003 SET collation_connection = @saved_col_connection */ ; 
/*!50003 DROP FUNCTION IF EXISTS `INET_CTOA` */; 
/*!50003 SET @saved_cs_client  = @@character_set_client */ ; 
/*!50003 SET @saved_cs_results  = @@character_set_results */ ; 
/*!50003 SET @saved_col_connection = @@collation_connection */ ; 
/*!50003 SET character_set_client = utf8 */ ; 
/*!50003 SET character_set_results = utf8 */ ; 
/*!50003 SET collation_connection = utf8_general_ci */ ; 
/*!50003 SET @saved_sql_mode  = @@sql_mode */ ; 
/*!50003 SET sql_mode    = 'ALLOW_INVALID_DATES' */ ; 
DELIMITER ;; 
CREATE DEFINER=`root`@`localhost` FUNCTION `INET_CTOA`(`paramCIDR` int) RETURNS varchar(15) CHARSET utf8 
    DETERMINISTIC 
    COMMENT 'Converts a CIDR suffix (integer between 0 and 32) to an IPv4 netmask in dotted decimal notation' 
BEGIN 
    DECLARE `netmask` int unsigned; 
    IF ((`paramCIDR` < 0) OR (`paramCIDR` > 32)) 
    THEN 
     RETURN NULL; 
    ELSE 
     SET `netmask` = 0xFFFFFFFF - (pow(2, (32-`paramCIDR`)) - 1); 
     RETURN INET_NTOA(`netmask`); 
    END IF; 
END ;; 
DELIMITER ; 
/*!50003 SET sql_mode    = @saved_sql_mode */ ; 
/*!50003 SET character_set_client = @saved_cs_client */ ; 
/*!50003 SET character_set_results = @saved_cs_results */ ; 
/*!50003 SET collation_connection = @saved_col_connection */ ; 
/*!50003 DROP PROCEDURE IF EXISTS `getSubnet` */; 
/*!50003 SET @saved_cs_client  = @@character_set_client */ ; 
/*!50003 SET @saved_cs_results  = @@character_set_results */ ; 
/*!50003 SET @saved_col_connection = @@collation_connection */ ; 
/*!50003 SET character_set_client = utf8 */ ; 
/*!50003 SET character_set_results = utf8 */ ; 
/*!50003 SET collation_connection = utf8_general_ci */ ; 
/*!50003 SET @saved_sql_mode  = @@sql_mode */ ; 
/*!50003 SET sql_mode    = '' */ ; 
DELIMITER ;; 
CREATE DEFINER=`root`@`localhost` PROCEDURE `getSubnet`(INOUT `paramADDR` VARCHAR(15), INOUT `paramCIDR` INT, OUT `paramMASK` VARCHAR(15), OUT `paramNETWORK` VARCHAR(15), OUT `paramBROADCAST` VARCHAR(15), OUT `paramNUMHOSTS` INT) CHARSET utf8 
    DETERMINISTIC 
BEGIN 
    DECLARE `numaddrs` int unsigned; 
    DECLARE `ipaddr` int unsigned; 
    DECLARE `netmask` int unsigned; 
    DECLARE `wildcard` int unsigned; 
    DECLARE `network` int unsigned; 
    DECLARE `broadcast` int unsigned; 
    DECLARE `numhosts` int unsigned; 

    SET `ipaddr` = INET_ATON(`paramADDR`); 

    IF (`ipaddr` IS NULL) OR (`paramCIDR` < 1) OR (`paramCIDR` > 30) 
    THEN 
     SELECT 
      NULL, NULL, NULL, NULL, NULL, NULL 
     INTO 
      `paramADDR`, `paramCIDR`, `paramMASK`, `paramNETWORK`, `paramBROADCAST`, `paramNUMHOSTS`; 
    ELSE 
     SET `numaddrs` = pow(2, (32-`paramCIDR`)); 
     SET `numhosts` = `numaddrs` - 2; 
     SET `netmask` = 0xFFFFFFFF - (`numaddrs` - 1); 
     SET `wildcard` = 0xFFFFFFFF & (~`netmask`); 
     SET `network` = `ipaddr` & `netmask`; 
     SET `broadcast` = `ipaddr` | `wildcard`; 

     SELECT 
      INET_NTOA(`ipaddr`), `paramCIDR`, INET_NTOA(`netmask`), INET_NTOA(`network`), INET_NTOA(`broadcast`), `numhosts` 
     INTO 
      `paramADDR`, `paramCIDR`, `paramMASK`, `paramNETWORK`, `paramBROADCAST`, `paramNUMHOSTS`; 
    END IF; 
END ;; 
DELIMITER ; 
/*!50003 SET sql_mode    = @saved_sql_mode */ ; 
/*!50003 SET character_set_client = @saved_cs_client */ ; 
/*!50003 SET character_set_results = @saved_cs_results */ ; 
/*!50003 SET collation_connection = @saved_col_connection */ ; 
/*!40103 SET [email protected]_TIME_ZONE */; 

/*!40101 SET [email protected]_SQL_MODE */; 
/*!40014 SET [email protected]_FOREIGN_KEY_CHECKS */; 
/*!40014 SET [email protected]_UNIQUE_CHECKS */; 
/*!40101 SET [email protected]_CHARACTER_SET_CLIENT */; 
/*!40101 SET [email protected]_CHARACTER_SET_RESULTS */; 
/*!40101 SET [email protected]_COLLATION_CONNECTION */; 
/*!40111 SET [email protected]_SQL_NOTES */; 
0

par exemple vous devez convertir le masque de réseau 255.255.255.252 en masque de bits.

J'utilise toujours cette requête simple (PostgreSQL):

SELECT 32-length(trim(((split_part('255.255.255.252','.',1)::bigint*(256^3)::bigint + 
         split_part('255.255.255.252','.',2)::bigint*(256^2)::bigint + 
         split_part('255.255.255.252','.',3)::bigint*256 + 
         split_part('255.255.255.252','.',4)::bigint)::bit(32))::text,'1')); 

pas aussi belle que pourrait être, mais il est court et travailler comme un charme ..

Questions connexes