Utilisation de SQL pour déterminer la valeur cidr d'un masque de sous-réseau
Question
J'aimerais trouver un moyen de faire une requête SQL qui calculera le cidr (représentation en bits) d'un masque de sous-réseau stocké dans la base de données. Ainsi, par exemple, 255.255.255.0 ou sa valeur décimale (4294967040) est stockée dans la base de données. Je souhaite effectuer une sélection et obtenir une représentation / 24 suivante via la requête.
J'ai effectué les opérations suivantes pour déterminer la dernière adresse IP d'un sous-réseau. 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, il s'agira d'une instruction SQL fonctionnant sous mysql, postgres, oracle, etc.
La solution
Je pense avoir trouvé la solution à mon problème. Voici ce que j'ai fait:
select CONCAT(INET_NTOA(ip_addr),'/',32-log2((4294967296-ip_mask))) net
from subnets
order by ip_addr
En gros, je prends mon masque décimal et le soustrait de la valeur décimale maximale. J'ai ensuite à un log2 sur cette valeur pour obtenir la valeur de logarithme. Puis soustrayez simplement cela de 32 (le bit maximum disponible).
L’espoir qui aide les autres.
Merci
Autres conseils
Les requêtes SQL n'ont pas de construction de boucle procédurale (nonobstant le langage procédural), mais vous pouvez comparer un jeu de lignes à un autre, ce qui est un peu comme une boucle.
Vous n'avez que 32 masques de sous-réseau possibles. Dans ce cas, il est judicieux de créer une petite table contenant 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));
par exemple. vous devez convertir 255.255.255.252
masque de réseau 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 beau qu'on pourrait l'être, mais c'est court et fonctionne comme un charme ..
--
-- 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 TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;