2008-11-10 47 views
11

Tôi muốn tìm cách thực hiện truy vấn SQL để tính toán cidr (biểu diễn bit) của mặt nạ mạng con được lưu trữ trong cơ sở dữ liệu. Vì vậy, ví dụ, tôi đã có 255.255.255.0 hoặc giá trị thập phân của nó (4294967040) được lưu trữ trong cơ sở dữ liệu. Tôi muốn làm một lựa chọn và lấy lại/24 đại diện thông qua truy vấn.Sử dụng SQL để xác định giá trị cidr của mặt nạ mạng con

Tôi đã thực hiện những việc như sau để xác định IP cuối cùng của mạng con vì vậy tôi hy vọng sẽ làm điều gì đó tương tự để xác định biểu diễn cidr của mặt nạ.

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

Tốt đây sẽ là một câu lệnh SQL mà sẽ làm việc dưới mysql, postgres, oracle, vv

Trả lời

8

Tôi nghĩ rằng tôi đã tìm ra giải pháp cho vấn đề của mình. Đây là những gì tôi đã làm:

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

Về cơ bản tôi lấy mặt nạ thập phân và trừ nó khỏi giá trị thập phân tối đa. Tôi sau đó đến một log2 trên giá trị đó để có được giá trị logarit. Sau đó, chỉ cần trừ nó từ 32 (bit tối đa có sẵn).

Hy vọng rằng sẽ giúp người khác.

Cảm ơn

+0

log2 ... tuyệt vời! :-) –

+0

Tôi đã kiểm tra điều này với tất cả các giá trị trong bảng này và thấy nó là chính xác: https://kthx.at/subnetmask/. – apostl3pol

2

truy vấn SQL không có một cấu trúc vòng lặp thủ tục (mặc dù ngôn ngữ thủ tục), nhưng bạn có thể so sánh một thiết lập các hàng cho một tập hợp các hàng khác, giống như một vòng lặp.

Bạn chỉ có 32 mặt nạ mạng phụ có thể. Trong trường hợp như thế này, bạn nên tạo một bảng nhỏ lưu trữ 32 mặt nạ này và số CIDR liên quan.

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

Thêm một bảng được những gì tôi đã hy vọng tránh. Tôi đã hy vọng có thêm các phép tính toán học có thể được thực hiện để chuyển đổi từ thập phân sang nhị phân, sau đó đếm các bit. Cảm ơn mặc dù, điều này là hữu ích –

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

ví dụ bạn cần chuyển đổi 255.255.255.252 mặt nạ mạng thành mặt nạ bit.

tôi luôn luôn sử dụng truy vấn này đơn giản (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')); 

không phải là đẹp biết mấy, nhưng nó ngắn và làm việc như một nét duyên dáng ..

Các vấn đề liên quan