2013-05-17 52 views
3

我有一個MySQL黑名單表,其中包含單個IPv4,IPv6或其中一種類型的CIDR範圍。在CIDR範圍內找到IPv6的正確方法MySQL

我的表看起來有點這樣的:

+-----------+-------------+ 
| Name  | Type  | 
+-----------+-------------+ 
| IpAddress | VARCHAR(46) | 
+-----------+-------------+ 
| Mask  | INT(2)  | 
+-----------+-------------+ 

現在,我想檢查一個給定的IP地址是否是在保存的CIDR的範圍之一。 我已經做到了這一點後,研究了一大堆的頁面了幾天,最後只移植了以下解決方案到MySQL:PHP5 calculate IPv6 range from cidr prefix?

所以我變換IP + CIDR掩碼的第一和最後一個IP在這個範圍內,然後我使用INET6_ATON將其轉換爲數字,並將其與BETWEEN運算符進行比較。

我的實現:

獲取最後一個IP

FUNCTION (`Ip` VARCHAR(46), `Mask` INT(2) UNSIGNED) RETURNS varchar(39) 
BEGIN 
    DECLARE IpNumber VARBINARY(16); 
    DECLARE Last VARCHAR(39) DEFAULT ''; 
    DECLARE FlexBits, Counter, Deci, NewByte INT UNSIGNED; 
    DECLARE HexIp VARCHAR(32); 

    SET IpNumber = INET6_ATON(Ip); 
    SET HexIp = HEX(IpNumber); 
    SET FlexBits = 128 - Mask; 
    SET Counter = 32; 

    WHILE (FlexBits > 0) DO 
     SET Deci = CONV(SUBSTR(HexIp, Counter, 1), 16, 10); 
     SET NewByte = Deci | (POW(2, LEAST(4, FlexBits)) - 1); 
     SET Last = CONCAT(CONV(NewByte, 10, 16), Last); 

     IF FlexBits >= 4 THEN SET FlexBits = FlexBits - 4; 
     ELSE SET FlexBits = 0; 
     END IF; 

     SET Counter = Counter - 1; 
    END WHILE; 

    SET Last = CONCAT(SUBSTR(HexIp, 1, Counter), Last); 

    RETURN INET6_NTOA(UNHEX(Last)); 
END 

獲取第一個IP

FUNCTION (`Ip` VARCHAR(46), `Mask` INT(2) UNSIGNED, `WithMask` BOOLEAN) RETURNS varchar(39) 
BEGIN 
    DECLARE First VARCHAR (42) DEFAULT ''; 

    SET First = INET6_NTOA(UNHEX(RPAD(SUBSTR(HEX(INET6_ATON(Ip)), 1, Mask/4), 32, 0))); 

    IF (WithMask = 1) THEN 
     SET First = CONCAT(First, '/', CAST(Mask AS CHAR)); 
    END IF; 

    RETURN First; 
END 

這工作得很好!我只是有一個想法,即使用一些聰明的位操作可以更高效地完成它。 我讀過很多關於這個主題的問題,但我沒有真正找到具體的解決方案 任何幫助正確的方向將非常感激!

注意:只有IPv6,我已經實現了IPv4的正確方式。

+0

我知道這是非常古老的,但有MYSQL改變了它的功能嗎? 5.6根本不會接受這個。 –

回答

1

我已經在大約一年前以相同的方式實現了這一點。所以這個想法很好。但是,在你的代碼中有一個小而重要的錯誤。

線:

SET最後= CONCAT(最後,CONV(NewByte,10,16));

而應被:

SET最後= CONCAT(CONV(NewByte,10,16),最後);

+0

看得見,謝謝!這可能會在未來的某個地方造成一個真實的臉譜時刻。雖然對於你輸入的大部分IP來說並不重要。 – Milananas

1

我找到了你的獲取最後一個ip函數的一個bug。

,而不是這樣的:

SET FlexBits = FlexBits - 4; 

使用這樣的:當你試圖讓

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '([email protected] - 4)' 

IF FlexBits >= 4 THEN SET FlexBits = FlexBits - 4; 
ELSE SET FlexBits = 0; 
END IF; 

否則,你會得到這樣的錯誤信息這些IPv6範圍的最後一個IP(掩碼c注:除以4):

SELECT getLastIp("2001:200:1::", 47); 
SELECT getLastIp("2001:200:0:8000", 49); 
SELECT getLastIp("2001:470:1f06:2000::", 51); 
SELECT getLastIp("2001:470:0:284:2::", 79); 
SELECT getLastIp("2001:470:1f08:415::8:0", 109); 
SELECT getLastIp("2001:550:0:1000::8275:8000", 113); 
SELECT getLastIp("2001:550:0:1000::9a19:300", 123); 
SELECT getLastIp("2001:550:0:1000::9a19:320", 126); 
+0

正確!將它添加到代碼中,謝謝。 – Milananas